--- /dev/null
+# Makefile for C source code
+#
+# Copyright (C) 2011 Colin Walters <walters@verbum.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# Author: Colin Walters <walters@verbum.org>
+
+noinst_LTLIBRARIES += libostree.la
+
+libostree_la_SOURCES = libostree/ostree.h \
+ libostree/ostree-core.c \
+ libostree/ostree-core.h \
+ libostree/ostree-repo.c \
+ libostree/ostree-repo.h \
+ libostree/ostree-checkout.c \
+ libostree/ostree-checkout.h \
+ $(NULL)
+libostree_la_CFLAGS = -I$(srcdir)/libotutil -I$(srcdir)/libostree -DLOCALEDIR=\"$(datadir)/locale\" $(OT_COREBIN_DEP_CFLAGS)
+libostree_la_LIBADD = libotutil.la $(OT_COREBIN_DEP_LIBS)
--- /dev/null
+# Makefile for C source code
+#
+# Copyright (C) 2011 Colin Walters <walters@verbum.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# Author: Colin Walters <walters@verbum.org>
+
+bin_PROGRAMS += ostree
+
+ostree_SOURCES = ostree/main.c \
+ ostree/ot-builtins.h \
+ ostree/ot-builtin-checkout.c \
+ ostree/ot-builtin-commit.c \
+ ostree/ot-builtin-fsck.c \
+ ostree/ot-builtin-init.c \
+ ostree/ot-builtin-link-file.c \
+ ostree/ot-builtin-log.c \
+ ostree/ot-builtin-pull.c \
+ ostree/ot-builtin-run-triggers.c \
+ ostree/ot-builtin-remote.c \
+ ostree/ot-builtin-rev-parse.c \
+ ostree/ot-builtin-show.c \
+ $(NULL)
+ostree_CFLAGS = -I$(srcdir)/src -I$(srcdir)/libostree -I$(srcdir)/libotutil -DLOCALEDIR=\"$(datadir)/locale\" $(OT_COREBIN_DEP_CFLAGS)
+ostree_LDADD = libotutil.la libostree.la $(OT_COREBIN_DEP_LIBS)
--- /dev/null
+# Makefile for C source code
+#
+# Copyright (C) 2011 Colin Walters <walters@verbum.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# Author: Colin Walters <walters@verbum.org>
+
+noinst_LTLIBRARIES += libotutil.la
+
+libotutil_la_SOURCES = \
+ libotutil/ot-opt-utils.c \
+ libotutil/ot-opt-utils.h \
+ libotutil/ot-unix-utils.c \
+ libotutil/ot-unix-utils.h \
+ libotutil/ot-gio-utils.c \
+ libotutil/ot-gio-utils.h \
+ libotutil/otutil.h \
+ $(NULL)
+libotutil_la_CFLAGS = -I$(srcdir)/libotutil -DLOCALEDIR=\"$(datadir)/locale\" $(GIO_UNIX_CFLAGS)
+libotutil_la_LIBADD = $(GIO_UNIX_LIBS)
+++ /dev/null
-# Makefile for C source code
-#
-# Copyright (C) 2011 Colin Walters <walters@verbum.org>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-#
-# Author: Colin Walters <walters@verbum.org>
-
-noinst_LTLIBRARIES += libotutil.la
-
-libotutil_la_SOURCES = \
- src/libotutil/ot-opt-utils.c \
- src/libotutil/ot-opt-utils.h \
- src/libotutil/ot-unix-utils.c \
- src/libotutil/ot-unix-utils.h \
- src/libotutil/ot-gio-utils.c \
- src/libotutil/ot-gio-utils.h \
- src/libotutil/otutil.h \
- $(NULL)
-libotutil_la_CFLAGS = -I$(srcdir)/src/libotutil -DLOCALEDIR=\"$(datadir)/locale\" $(GIO_UNIX_CFLAGS)
-libotutil_la_LIBADD = $(GIO_UNIX_LIBS)
-
-noinst_LTLIBRARIES += libostree.la
-
-libostree_la_SOURCES = src/libostree/ostree.h \
- src/libostree/ostree-core.c \
- src/libostree/ostree-core.h \
- src/libostree/ostree-repo.c \
- src/libostree/ostree-repo.h \
- src/libostree/ostree-checkout.c \
- src/libostree/ostree-checkout.h \
- $(NULL)
-libostree_la_CFLAGS = -I$(srcdir)/src/libostree -I$(srcdir)/src/libotutil -DLOCALEDIR=\"$(datadir)/locale\" $(OT_COREBIN_DEP_CFLAGS)
-libostree_la_LIBADD = libotutil.la $(OT_COREBIN_DEP_LIBS)
-
-bin_PROGRAMS += ostree
-
-ostree_SOURCES = src/main.c \
- src/ot-builtins.h \
- src/ot-builtin-checkout.c \
- src/ot-builtin-commit.c \
- src/ot-builtin-fsck.c \
- src/ot-builtin-init.c \
- src/ot-builtin-link-file.c \
- src/ot-builtin-log.c \
- src/ot-builtin-pull.c \
- src/ot-builtin-run-triggers.c \
- src/ot-builtin-remote.c \
- src/ot-builtin-rev-parse.c \
- src/ot-builtin-show.c \
- $(NULL)
-ostree_CFLAGS = -I$(srcdir)/src -I$(srcdir)/src/libostree -I$(srcdir)/src/libotutil -DLOCALEDIR=\"$(datadir)/locale\" $(OT_COREBIN_DEP_CFLAGS)
-ostree_LDADD = libotutil.la libostree.la $(OT_COREBIN_DEP_LIBS)
noinst_LTLIBRARIES =
noinst_PROGRAMS =
-include Makefile-src.am
+include Makefile-otutil.am
+include Makefile-libostree.am
+include Makefile-ostree.am
include Makefile-triggers.am
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ostree.h"
+#include "otutil.h"
+
+enum {
+ PROP_0,
+
+ PROP_REPO,
+ PROP_PATH
+};
+
+G_DEFINE_TYPE (OstreeCheckout, ostree_checkout, G_TYPE_OBJECT)
+
+#define GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), OSTREE_TYPE_CHECKOUT, OstreeCheckoutPrivate))
+
+typedef struct _OstreeCheckoutPrivate OstreeCheckoutPrivate;
+
+struct _OstreeCheckoutPrivate {
+ OstreeRepo *repo;
+ char *path;
+};
+
+static void
+ostree_checkout_finalize (GObject *object)
+{
+ OstreeCheckout *self = OSTREE_CHECKOUT (object);
+ OstreeCheckoutPrivate *priv = GET_PRIVATE (self);
+
+ g_free (priv->path);
+ g_clear_object (&priv->repo);
+
+ G_OBJECT_CLASS (ostree_checkout_parent_class)->finalize (object);
+}
+
+static void
+ostree_checkout_set_property(GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ OstreeCheckout *self = OSTREE_CHECKOUT (object);
+ OstreeCheckoutPrivate *priv = GET_PRIVATE (self);
+
+ switch (prop_id)
+ {
+ case PROP_PATH:
+ priv->path = g_value_dup_string (value);
+ break;
+ case PROP_REPO:
+ priv->repo = g_value_dup_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+ostree_checkout_get_property(GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ OstreeCheckout *self = OSTREE_CHECKOUT (object);
+ OstreeCheckoutPrivate *priv = GET_PRIVATE (self);
+
+ switch (prop_id)
+ {
+ case PROP_PATH:
+ g_value_set_string (value, priv->path);
+ break;
+ case PROP_REPO:
+ g_value_set_object (value, priv->repo);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static GObject *
+ostree_checkout_constructor (GType gtype,
+ guint n_properties,
+ GObjectConstructParam *properties)
+{
+ GObject *object;
+ GObjectClass *parent_class;
+ OstreeCheckoutPrivate *priv;
+
+ parent_class = G_OBJECT_CLASS (ostree_checkout_parent_class);
+ object = parent_class->constructor (gtype, n_properties, properties);
+
+ priv = GET_PRIVATE (object);
+
+ g_assert (priv->path != NULL);
+
+ return object;
+}
+
+static void
+ostree_checkout_class_init (OstreeCheckoutClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (OstreeCheckoutPrivate));
+
+ object_class->constructor = ostree_checkout_constructor;
+ object_class->get_property = ostree_checkout_get_property;
+ object_class->set_property = ostree_checkout_set_property;
+ object_class->finalize = ostree_checkout_finalize;
+
+ g_object_class_install_property (object_class,
+ PROP_PATH,
+ g_param_spec_string ("path", "", "",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property (object_class,
+ PROP_REPO,
+ g_param_spec_object ("repo", "", "",
+ OSTREE_TYPE_REPO,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+ostree_checkout_init (OstreeCheckout *self)
+{
+}
+
+OstreeCheckout*
+ostree_checkout_new (OstreeRepo *repo,
+ const char *path)
+{
+ return g_object_new (OSTREE_TYPE_CHECKOUT, "repo", repo, "path", path, NULL);
+}
+
+static gboolean
+executable_exists_in_checkout (const char *path,
+ const char *executable)
+{
+ int i;
+ const char *subdirs[] = {"bin", "sbin", "usr/bin", "usr/sbin"};
+
+ for (i = 0; i < G_N_ELEMENTS (subdirs); i++)
+ {
+ char *possible_path = g_build_filename (path, subdirs[i], executable, NULL);
+ gboolean exists;
+
+ exists = g_file_test (possible_path, G_FILE_TEST_EXISTS);
+ g_free (possible_path);
+
+ if (exists)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+run_trigger (OstreeCheckout *self,
+ GFile *trigger,
+ gboolean requires_chroot,
+ GError **error)
+{
+ OstreeCheckoutPrivate *priv = GET_PRIVATE (self);
+ gboolean ret = FALSE;
+ char *path = NULL;
+ char *temp_path = NULL;
+ char *rel_temp_path = NULL;
+ GFile *temp_copy = NULL;
+ char *basename = NULL;
+ GPtrArray *args = NULL;
+ int estatus;
+
+ path = g_file_get_path (trigger);
+ basename = g_path_get_basename (path);
+
+ args = g_ptr_array_new ();
+
+ if (requires_chroot)
+ {
+ temp_path = g_build_filename (priv->path, basename, NULL);
+ rel_temp_path = g_strconcat ("./", basename, NULL);
+ temp_copy = ot_util_new_file_for_path (temp_path);
+
+ if (!g_file_copy (trigger, temp_copy, 0, NULL, NULL, NULL, error))
+ goto out;
+
+ g_ptr_array_add (args, "chroot");
+ g_ptr_array_add (args, ".");
+ g_ptr_array_add (args, rel_temp_path);
+ g_ptr_array_add (args, NULL);
+ }
+ else
+ {
+ g_ptr_array_add (args, path);
+ g_ptr_array_add (args, NULL);
+ }
+
+ g_print ("Running trigger: %s\n", path);
+ if (!g_spawn_sync (priv->path,
+ (char**)args->pdata,
+ NULL,
+ G_SPAWN_SEARCH_PATH,
+ NULL, NULL, NULL, NULL,
+ &estatus,
+ error))
+ {
+ g_prefix_error (error, "Failed to run trigger %s: ", basename);
+ goto out;
+ }
+
+ ret = TRUE;
+ out:
+ if (requires_chroot && temp_path)
+ (void)unlink (temp_path);
+
+ g_free (path);
+ g_free (basename);
+ g_free (temp_path);
+ g_free (rel_temp_path);
+ g_clear_object (&temp_copy);
+ if (args)
+ g_ptr_array_free (args, TRUE);
+ return ret;
+}
+
+static gboolean
+check_trigger (OstreeCheckout *self,
+ GFile *trigger,
+ GError **error)
+{
+ OstreeCheckoutPrivate *priv = GET_PRIVATE (self);
+ gboolean ret = FALSE;
+ GInputStream *instream = NULL;
+ GDataInputStream *datain = NULL;
+ GError *temp_error = NULL;
+ char *line;
+ gsize len;
+ gboolean requires_chroot = TRUE;
+ gboolean matches = FALSE;
+
+ instream = (GInputStream*)g_file_read (trigger, NULL, error);
+ if (!instream)
+ goto out;
+ datain = g_data_input_stream_new (instream);
+
+ while ((line = g_data_input_stream_read_line (datain, &len, NULL, &temp_error)) != NULL)
+ {
+ if (g_str_has_prefix (line, "# IfExecutable: "))
+ {
+ char *executable = g_strdup (line + strlen ("# IfExecutable: "));
+ g_strchomp (executable);
+ matches = executable_exists_in_checkout (priv->path, executable);
+ g_free (executable);
+ }
+
+ g_free (line);
+ }
+ if (line == NULL && temp_error != NULL)
+ {
+ g_propagate_error (error, temp_error);
+ goto out;
+ }
+ if (matches)
+ {
+ if (!run_trigger (self, trigger, requires_chroot, error))
+ goto out;
+ }
+
+ ret = TRUE;
+ out:
+ g_clear_object (&instream);
+ g_clear_object (&datain);
+ return ret;
+}
+
+gboolean
+ostree_checkout_run_triggers (OstreeCheckout *self,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GError *temp_error = NULL;
+ char *triggerdir_path = NULL;
+ GFile *triggerdir = NULL;
+ GFileInfo *file_info = NULL;
+ GFileEnumerator *enumerator = NULL;
+
+ triggerdir_path = g_build_filename (LIBEXECDIR, "ostree", "triggers.d", NULL);
+ triggerdir = ot_util_new_file_for_path (triggerdir_path);
+
+ enumerator = g_file_enumerate_children (triggerdir, "standard::name,standard::type,unix::*",
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ NULL,
+ error);
+ if (!enumerator)
+ goto out;
+
+ while ((file_info = g_file_enumerator_next_file (enumerator, NULL, &temp_error)) != NULL)
+ {
+ const char *name;
+ guint32 type;
+ char *child_path = NULL;
+ GFile *child = NULL;
+ gboolean success;
+
+ name = g_file_info_get_attribute_byte_string (file_info, "standard::name");
+ type = g_file_info_get_attribute_uint32 (file_info, "standard::type");
+
+ if (type == G_FILE_TYPE_REGULAR && g_str_has_suffix (name, ".trigger"))
+ {
+ child_path = g_build_filename (triggerdir_path, name, NULL);
+ child = ot_util_new_file_for_path (child_path);
+
+ success = check_trigger (self, child, error);
+ }
+ else
+ success = TRUE;
+
+ g_object_unref (file_info);
+ g_free (child_path);
+ g_clear_object (&child);
+ if (!success)
+ goto out;
+ }
+ if (file_info == NULL && temp_error != NULL)
+ {
+ g_propagate_error (error, temp_error);
+ goto out;
+ }
+
+ ret = TRUE;
+ out:
+ g_free (triggerdir_path);
+ g_clear_object (&triggerdir);
+ g_clear_object (&enumerator);
+ return ret;
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef _OSTREE_CHECKOUT
+#define _OSTREE_CHECKOUT
+
+#include <ostree-repo.h>
+
+G_BEGIN_DECLS
+
+#define OSTREE_TYPE_CHECKOUT ostree_checkout_get_type()
+#define OSTREE_CHECKOUT(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), OSTREE_TYPE_CHECKOUT, OstreeCheckout))
+#define OSTREE_CHECKOUT_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), OSTREE_TYPE_CHECKOUT, OstreeCheckoutClass))
+#define OSTREE_IS_CHECKOUT(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), OSTREE_TYPE_CHECKOUT))
+#define OSTREE_IS_CHECKOUT_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), OSTREE_TYPE_CHECKOUT))
+#define OSTREE_CHECKOUT_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), OSTREE_TYPE_CHECKOUT, OstreeCheckoutClass))
+
+typedef struct {
+ GObject parent;
+} OstreeCheckout;
+
+typedef struct {
+ GObjectClass parent_class;
+} OstreeCheckoutClass;
+
+GType ostree_checkout_get_type (void);
+
+OstreeCheckout* ostree_checkout_new (OstreeRepo *repo,
+ const char *path);
+
+gboolean ostree_checkout_run_triggers (OstreeCheckout *checkout,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* _OSTREE_CHECKOUT */
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ostree.h"
+#include "otutil.h"
+
+#include <sys/types.h>
+#include <attr/xattr.h>
+
+gboolean
+ostree_validate_checksum_string (const char *sha256,
+ GError **error)
+{
+ if (strlen (sha256) != 64)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Invalid rev '%s'", sha256);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+void
+ostree_checksum_update_stat (GChecksum *checksum, guint32 uid, guint32 gid, guint32 mode)
+{
+ guint32 perms = (mode & ~S_IFMT);
+ g_checksum_update (checksum, (guint8*) &uid, 4);
+ g_checksum_update (checksum, (guint8*) &gid, 4);
+ g_checksum_update (checksum, (guint8*) &perms, 4);
+}
+
+static char *
+canonicalize_xattrs (char *xattr_string, size_t len)
+{
+ char *p;
+ GSList *xattrs = NULL;
+ GSList *iter;
+ GString *result;
+
+ result = g_string_new (0);
+
+ p = xattr_string;
+ while (p < xattr_string+len)
+ {
+ xattrs = g_slist_prepend (xattrs, p);
+ p += strlen (p) + 1;
+ }
+
+ xattrs = g_slist_sort (xattrs, (GCompareFunc) strcmp);
+ for (iter = xattrs; iter; iter = iter->next)
+ g_string_append (result, iter->data);
+
+ g_slist_free (xattrs);
+ return g_string_free (result, FALSE);
+}
+
+static gboolean
+read_xattr_name_array (const char *path,
+ const char *xattrs,
+ size_t len,
+ GVariantBuilder *builder,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ const char *p;
+
+ p = xattrs;
+ while (p < xattrs+len)
+ {
+ ssize_t bytes_read;
+ char *buf;
+
+ bytes_read = lgetxattr (path, p, NULL, 0);
+ if (bytes_read < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ if (bytes_read == 0)
+ continue;
+
+ buf = g_malloc (bytes_read);
+ if (lgetxattr (path, p, buf, bytes_read) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ g_free (buf);
+ goto out;
+ }
+
+ g_variant_builder_add (builder, "(@ay@ay)",
+ g_variant_new_bytestring (p),
+ g_variant_new_from_data (G_VARIANT_TYPE ("ay"),
+ buf, bytes_read, FALSE, g_free, buf));
+
+ p = p + strlen (p) + 1;
+ }
+
+ ret = TRUE;
+ out:
+ return ret;
+}
+
+GVariant *
+ostree_get_xattrs_for_path (const char *path,
+ GError **error)
+{
+ GVariant *ret = NULL;
+ GVariantBuilder builder;
+ char *xattr_names = NULL;
+ char *xattr_names_canonical = NULL;
+ ssize_t bytes_read;
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ayay)"));
+
+ bytes_read = llistxattr (path, NULL, 0);
+
+ if (bytes_read < 0)
+ {
+ if (errno != ENOTSUP)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ }
+ else if (bytes_read > 0)
+ {
+ xattr_names = g_malloc (bytes_read);
+ if (llistxattr (path, xattr_names, bytes_read) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ xattr_names_canonical = canonicalize_xattrs (xattr_names, bytes_read);
+
+ if (!read_xattr_name_array (path, xattr_names_canonical, bytes_read, &builder, error))
+ goto out;
+ }
+
+ ret = g_variant_builder_end (&builder);
+ g_variant_ref_sink (ret);
+ out:
+ if (!ret)
+ g_variant_builder_clear (&builder);
+ g_free (xattr_names);
+ g_free (xattr_names_canonical);
+ return ret;
+}
+
+gboolean
+ostree_stat_and_checksum_file (int dir_fd, const char *path,
+ OstreeObjectType objtype,
+ GChecksum **out_checksum,
+ struct stat *out_stbuf,
+ GError **error)
+{
+ GChecksum *content_sha256 = NULL;
+ GChecksum *content_and_meta_sha256 = NULL;
+ char *stat_string = NULL;
+ ssize_t bytes_read;
+ GVariant *xattrs = NULL;
+ int fd = -1;
+ DIR *temp_dir = NULL;
+ char *basename = NULL;
+ gboolean ret = FALSE;
+ char *symlink_target = NULL;
+ char *device_id = NULL;
+ struct stat stbuf;
+
+ basename = g_path_get_basename (path);
+
+ if (dir_fd == -1)
+ {
+ char *dirname = g_path_get_dirname (path);
+ temp_dir = opendir (dirname);
+ if (temp_dir == NULL)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ g_free (dirname);
+ }
+ g_free (dirname);
+ dir_fd = dirfd (temp_dir);
+ }
+
+ if (fstatat (dir_fd, basename, &stbuf, AT_SYMLINK_NOFOLLOW) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+
+ if (S_ISREG(stbuf.st_mode))
+ {
+ fd = ot_util_open_file_read_at (dir_fd, basename, error);
+ if (fd < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ }
+
+ if (objtype == OSTREE_OBJECT_TYPE_FILE)
+ {
+ xattrs = ostree_get_xattrs_for_path (path, error);
+ if (!xattrs)
+ goto out;
+ }
+
+ content_sha256 = g_checksum_new (G_CHECKSUM_SHA256);
+
+ if (S_ISREG(stbuf.st_mode))
+ {
+ guint8 buf[8192];
+
+ while ((bytes_read = read (fd, buf, sizeof (buf))) > 0)
+ g_checksum_update (content_sha256, buf, bytes_read);
+ if (bytes_read < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ }
+ else if (S_ISLNK(stbuf.st_mode))
+ {
+ symlink_target = g_malloc (PATH_MAX);
+
+ g_assert (objtype == OSTREE_OBJECT_TYPE_FILE);
+
+ bytes_read = readlinkat (dir_fd, basename, symlink_target, PATH_MAX);
+ if (bytes_read < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ g_checksum_update (content_sha256, (guint8*)symlink_target, bytes_read);
+ }
+ else if (S_ISCHR(stbuf.st_mode) || S_ISBLK(stbuf.st_mode))
+ {
+ g_assert (objtype == OSTREE_OBJECT_TYPE_FILE);
+ device_id = g_strdup_printf ("%u", (guint)stbuf.st_rdev);
+ g_checksum_update (content_sha256, (guint8*)device_id, strlen (device_id));
+ }
+ else
+ {
+ g_set_error (error, G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ "Unsupported file '%s' (must be regular, symbolic link, or device)",
+ path);
+ goto out;
+ }
+
+ content_and_meta_sha256 = g_checksum_copy (content_sha256);
+
+ if (objtype == OSTREE_OBJECT_TYPE_FILE)
+ {
+ ostree_checksum_update_stat (content_and_meta_sha256, stbuf.st_uid,
+ stbuf.st_gid, stbuf.st_mode);
+ g_checksum_update (content_and_meta_sha256, (guint8*)g_variant_get_data (xattrs), g_variant_get_size (xattrs));
+ }
+
+ *out_stbuf = stbuf;
+ *out_checksum = content_and_meta_sha256;
+ ret = TRUE;
+ out:
+ if (fd >= 0)
+ close (fd);
+ if (temp_dir != NULL)
+ closedir (temp_dir);
+ g_free (symlink_target);
+ g_free (basename);
+ g_free (stat_string);
+ if (xattrs)
+ g_variant_unref (xattrs);
+ if (content_sha256)
+ g_checksum_free (content_sha256);
+ return ret;
+}
+
+gboolean
+ostree_set_xattrs (const char *path, GVariant *xattrs, GError **error)
+{
+ gboolean ret = FALSE;
+ int i, n;
+
+ n = g_variant_n_children (xattrs);
+ for (i = 0; i < n; i++)
+ {
+ const guint8* name;
+ GVariant *value;
+ const guint8* value_data;
+ gsize value_len;
+ gboolean loop_err;
+
+ g_variant_get_child (xattrs, i, "(^&ay@ay)",
+ &name, &value);
+ value_data = g_variant_get_fixed_array (value, &value_len, 1);
+
+ loop_err = lsetxattr (path, (char*)name, (char*)value_data, value_len, XATTR_REPLACE) < 0;
+
+ g_variant_unref (value);
+ if (loop_err)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ }
+
+ ret = TRUE;
+ out:
+ return ret;
+}
+
+gboolean
+ostree_parse_metadata_file (const char *path,
+ OstreeSerializedVariantType *out_type,
+ GVariant **out_variant,
+ GError **error)
+{
+ GMappedFile *mfile = NULL;
+ gboolean ret = FALSE;
+ GVariant *ret_variant = NULL;
+ GVariant *container = NULL;
+ guint32 ret_type;
+
+ mfile = g_mapped_file_new (path, FALSE, error);
+ if (mfile == NULL)
+ {
+ goto out;
+ }
+ else
+ {
+ container = g_variant_new_from_data (G_VARIANT_TYPE (OSTREE_SERIALIZED_VARIANT_FORMAT),
+ g_mapped_file_get_contents (mfile),
+ g_mapped_file_get_length (mfile),
+ FALSE,
+ (GDestroyNotify) g_mapped_file_unref,
+ mfile);
+ mfile = NULL;
+ g_variant_ref_sink (container);
+ g_variant_get (container, "(uv)",
+ &ret_type, &ret_variant);
+ ret_type = GUINT32_FROM_BE (ret_type);
+ if (ret_type <= 0 || ret_type > OSTREE_SERIALIZED_VARIANT_LAST)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Corrupted metadata object '%s'; invalid type %d", path, ret_type);
+ goto out;
+ }
+ }
+
+ ret = TRUE;
+ *out_type = ret_type;
+ *out_variant = g_variant_ref_sink (ret_variant);
+ ret_variant = NULL;
+ out:
+ if (ret_variant)
+ g_variant_unref (ret_variant);
+ if (container != NULL)
+ g_variant_unref (container);
+ if (mfile != NULL)
+ g_mapped_file_unref (mfile);
+ return ret;
+}
+
+char *
+ostree_get_relative_object_path (const char *checksum,
+ OstreeObjectType type,
+ gboolean archive)
+{
+ GString *path;
+ const char *type_string;
+
+ g_assert (strlen (checksum) == 64);
+
+ path = g_string_new ("objects/");
+
+ g_string_append_len (path, checksum, 2);
+ g_string_append_c (path, '/');
+ g_string_append (path, checksum + 2);
+ switch (type)
+ {
+ case OSTREE_OBJECT_TYPE_FILE:
+ if (archive)
+ type_string = ".packfile";
+ else
+ type_string = ".file";
+ break;
+ case OSTREE_OBJECT_TYPE_META:
+ type_string = ".meta";
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+ g_string_append (path, type_string);
+ return g_string_free (path, FALSE);
+}
+
+gboolean
+ostree_pack_object (GOutputStream *output,
+ GFile *file,
+ OstreeObjectType objtype,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ char *path = NULL;
+ GFileInfo *finfo = NULL;
+ GFileInputStream *instream = NULL;
+ gboolean pack_builder_initialized = FALSE;
+ GVariantBuilder pack_builder;
+ GVariant *pack_variant = NULL;
+ GVariant *xattrs = NULL;
+ gsize bytes_written;
+
+ path = g_file_get_path (file);
+
+ finfo = g_file_query_info (file, "standard::type,standard::size,standard::is-symlink,standard::symlink-target,unix::*",
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, cancellable, error);
+ if (!finfo)
+ goto out;
+
+ if (objtype == OSTREE_OBJECT_TYPE_META)
+ {
+ guint64 object_size_be = GUINT64_TO_BE ((guint64)g_file_info_get_size (finfo));
+ if (!g_output_stream_write_all (output, &object_size_be, 8, &bytes_written, cancellable, error))
+ goto out;
+
+ instream = g_file_read (file, NULL, error);
+ if (!instream)
+ goto out;
+
+ if (g_output_stream_splice (output, (GInputStream*)instream, 0, cancellable, error) < 0)
+ goto out;
+ }
+ else
+ {
+ guint32 uid, gid, mode;
+ guint32 device = 0;
+ guint32 metadata_size_be;
+ const char *target = NULL;
+ guint64 object_size;
+
+ uid = g_file_info_get_attribute_uint32 (finfo, G_FILE_ATTRIBUTE_UNIX_UID);
+ gid = g_file_info_get_attribute_uint32 (finfo, G_FILE_ATTRIBUTE_UNIX_GID);
+ mode = g_file_info_get_attribute_uint32 (finfo, G_FILE_ATTRIBUTE_UNIX_MODE);
+
+ g_variant_builder_init (&pack_builder, G_VARIANT_TYPE (OSTREE_PACK_FILE_VARIANT_FORMAT));
+ pack_builder_initialized = TRUE;
+ g_variant_builder_add (&pack_builder, "u", GUINT32_TO_BE (0));
+ g_variant_builder_add (&pack_builder, "u", GUINT32_TO_BE (uid));
+ g_variant_builder_add (&pack_builder, "u", GUINT32_TO_BE (gid));
+ g_variant_builder_add (&pack_builder, "u", GUINT32_TO_BE (mode));
+
+ xattrs = ostree_get_xattrs_for_path (path, error);
+ if (!xattrs)
+ goto out;
+ g_variant_builder_add (&pack_builder, "@a(ayay)", xattrs);
+
+ if (S_ISREG (mode))
+ {
+ object_size = (guint64)g_file_info_get_size (finfo);
+ }
+ else if (S_ISLNK (mode))
+ {
+ target = g_file_info_get_attribute_byte_string (finfo, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET);
+ object_size = strlen (target);
+ }
+ else if (S_ISBLK (mode) || S_ISCHR (mode))
+ {
+ device = g_file_info_get_attribute_uint32 (finfo, G_FILE_ATTRIBUTE_UNIX_DEVICE);
+ object_size = 4;
+ }
+ else
+ g_assert_not_reached ();
+
+ g_variant_builder_add (&pack_builder, "t", GUINT64_TO_BE (object_size));
+ pack_variant = g_variant_builder_end (&pack_builder);
+ pack_builder_initialized = FALSE;
+
+ metadata_size_be = GUINT32_TO_BE (g_variant_get_size (pack_variant));
+
+ if (!g_output_stream_write_all (output, &metadata_size_be, 4,
+ &bytes_written, cancellable, error))
+ goto out;
+ g_assert (bytes_written == 4);
+
+ if (!g_output_stream_write_all (output, g_variant_get_data (pack_variant), g_variant_get_size (pack_variant),
+ &bytes_written, cancellable, error))
+ goto out;
+
+ if (S_ISREG (mode))
+ {
+ instream = g_file_read (file, NULL, error);
+ if (!instream)
+ goto out;
+ bytes_written = g_output_stream_splice (output, (GInputStream*)instream, 0, cancellable, error);
+ if (bytes_written < 0)
+ goto out;
+ if (bytes_written != object_size)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "File size changed unexpectedly");
+ goto out;
+ }
+ }
+ else if (S_ISLNK (mode))
+ {
+ if (!g_output_stream_write_all (output, target, object_size,
+ &bytes_written, cancellable, error))
+ goto out;
+ }
+ else if (S_ISBLK (mode) || S_ISCHR (mode))
+ {
+ guint32 device_be = GUINT32_TO_BE (device);
+ g_assert (object_size == 4);
+ if (!g_output_stream_write_all (output, &device_be, object_size,
+ &bytes_written, cancellable, error))
+ goto out;
+ g_assert (bytes_written == 4);
+ }
+ else
+ g_assert_not_reached ();
+ }
+
+ ret = TRUE;
+ out:
+ g_free (path);
+ g_clear_object (&finfo);
+ g_clear_object (&instream);
+ if (xattrs)
+ g_variant_unref (xattrs);
+ if (pack_builder_initialized)
+ g_variant_builder_clear (&pack_builder);
+ if (pack_variant)
+ g_variant_unref (pack_variant);
+ return ret;
+}
+
+static gboolean
+splice_and_checksum (GOutputStream *out,
+ GInputStream *in,
+ GChecksum *checksum,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+
+ if (checksum != NULL)
+ {
+ gsize bytes_read, bytes_written;
+ char buf[4096];
+ do
+ {
+ if (!g_input_stream_read_all (in, buf, sizeof(buf), &bytes_read, cancellable, error))
+ goto out;
+ if (checksum)
+ g_checksum_update (checksum, (guint8*)buf, bytes_read);
+ if (!g_output_stream_write_all (out, buf, bytes_read, &bytes_written, cancellable, error))
+ goto out;
+ }
+ while (bytes_read > 0);
+ }
+ else
+ {
+ if (g_output_stream_splice (out, in, 0, cancellable, error) < 0)
+ goto out;
+ }
+
+ ret = TRUE;
+ out:
+ return ret;
+}
+
+static gboolean
+unpack_meta (const char *path,
+ const char *dest_path,
+ GChecksum **out_checksum,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GFile *file = NULL;
+ GFile *dest_file = NULL;
+ GFileInputStream *in = NULL;
+ GChecksum *ret_checksum = NULL;
+ GFileOutputStream *out = NULL;
+
+ file = ot_util_new_file_for_path (path);
+ dest_file = ot_util_new_file_for_path (dest_path);
+
+ if (out_checksum)
+ ret_checksum = g_checksum_new (G_CHECKSUM_SHA256);
+
+ in = g_file_read (file, NULL, error);
+ if (!in)
+ goto out;
+
+ out = g_file_replace (dest_file, NULL, FALSE, 0, NULL, error);
+ if (!out)
+ goto out;
+
+ if (!splice_and_checksum ((GOutputStream*)out, (GInputStream*)in, ret_checksum, NULL, error))
+ goto out;
+
+ if (!g_output_stream_close ((GOutputStream*)out, NULL, error))
+ goto out;
+
+ ret = TRUE;
+ if (out_checksum)
+ *out_checksum = ret_checksum;
+ ret_checksum = NULL;
+ out:
+ if (!ret)
+ (void) unlink (dest_path);
+ if (ret_checksum)
+ g_checksum_free (ret_checksum);
+ g_clear_object (&file);
+ g_clear_object (&dest_file);
+ g_clear_object (&in);
+ return ret;
+}
+
+
+static gboolean
+unpack_file (const char *path,
+ const char *dest_path,
+ GChecksum **out_checksum,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GFile *file = NULL;
+ GFile *dest_file = NULL;
+ char *metadata_buf = NULL;
+ GVariant *metadata = NULL;
+ GVariant *xattrs = NULL;
+ GFileInputStream *in = NULL;
+ GFileOutputStream *out = NULL;
+ GChecksum *ret_checksum = NULL;
+ guint32 metadata_len;
+ guint32 version, uid, gid, mode;
+ guint64 content_len;
+ gsize bytes_read, bytes_written;
+ int temp_fd = -1;
+
+ file = ot_util_new_file_for_path (path);
+
+ in = g_file_read (file, NULL, error);
+ if (!in)
+ goto out;
+
+ if (!g_input_stream_read_all ((GInputStream*)in, &metadata_len, 4, &bytes_read, NULL, error))
+ goto out;
+ if (bytes_read != 4)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Corrupted packfile; too short while reading metadata length");
+ goto out;
+ }
+
+ metadata_len = GUINT32_FROM_BE (metadata_len);
+ metadata_buf = g_malloc (metadata_len);
+
+ if (!g_input_stream_read_all ((GInputStream*)in, metadata_buf, metadata_len, &bytes_read, NULL, error))
+ goto out;
+ if (bytes_read != metadata_len)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Corrupted packfile; too short while reading metadata");
+ goto out;
+ }
+
+ metadata = g_variant_new_from_data (G_VARIANT_TYPE (OSTREE_PACK_FILE_VARIANT_FORMAT),
+ metadata_buf, metadata_len, FALSE, NULL, NULL);
+
+ g_variant_get (metadata, "(uuuu@a(ayay)t)",
+ &version, &uid, &gid, &mode,
+ &xattrs, &content_len);
+ uid = GUINT32_FROM_BE (uid);
+ gid = GUINT32_FROM_BE (gid);
+ mode = GUINT32_FROM_BE (mode);
+ content_len = GUINT64_FROM_BE (content_len);
+
+ dest_file = ot_util_new_file_for_path (dest_path);
+
+ if (out_checksum)
+ ret_checksum = g_checksum_new (G_CHECKSUM_SHA256);
+
+ if (S_ISREG (mode))
+ {
+ out = g_file_replace (dest_file, NULL, FALSE, 0, NULL, error);
+ if (!out)
+ goto out;
+
+ if (!splice_and_checksum ((GOutputStream*)out, (GInputStream*)in, ret_checksum, NULL, error))
+ goto out;
+
+ if (!g_output_stream_close ((GOutputStream*)out, NULL, error))
+ goto out;
+ }
+ else if (S_ISLNK (mode))
+ {
+ char target[PATH_MAX+1];
+
+ if (!g_input_stream_read_all ((GInputStream*)in, target, sizeof(target)-1, &bytes_read, NULL, error))
+ goto out;
+ target[bytes_read] = '\0';
+ if (ret_checksum)
+ g_checksum_update (ret_checksum, (guint8*)target, bytes_read);
+ if (symlink (target, dest_path) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ }
+ else if (S_ISCHR (mode) || S_ISBLK (mode))
+ {
+ guint32 dev;
+
+ if (!g_input_stream_read_all ((GInputStream*)in, &dev, 4, &bytes_read, NULL, error))
+ goto out;
+ if (bytes_read != 4)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Corrupted packfile; too short while reading device id");
+ goto out;
+ }
+ dev = GUINT32_FROM_BE (dev);
+ if (ret_checksum)
+ g_checksum_update (ret_checksum, (guint8*)&dev, 4);
+ if (mknod (dest_path, mode, dev) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ }
+ else
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Corrupted packfile; invalid mode %u", mode);
+ goto out;
+ }
+
+ if (!S_ISLNK (mode))
+ {
+ if (chmod (dest_path, mode) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ }
+
+ if (!ostree_set_xattrs (dest_path, xattrs, error))
+ goto out;
+
+ if (ret_checksum)
+ {
+ ostree_checksum_update_stat (ret_checksum, uid, gid, mode);
+ g_checksum_update (ret_checksum, (guint8*)g_variant_get_data (xattrs), g_variant_get_size (xattrs));
+ }
+
+ ret = TRUE;
+ if (out_checksum)
+ *out_checksum = ret_checksum;
+ ret_checksum = NULL;
+ out:
+ if (!ret)
+ (void) unlink (dest_path);
+ if (ret_checksum)
+ g_checksum_free (ret_checksum);
+ g_free (metadata_buf);
+ g_clear_object (&file);
+ g_clear_object (&dest_file);
+ g_clear_object (&in);
+ g_clear_object (&out);
+ if (metadata)
+ g_variant_unref (metadata);
+ if (xattrs)
+ g_variant_unref (xattrs);
+ return ret;
+}
+
+gboolean
+ostree_unpack_object (const char *path,
+ OstreeObjectType objtype,
+ const char *dest_path,
+ GChecksum **out_checksum,
+ GError **error)
+{
+ if (objtype == OSTREE_OBJECT_TYPE_META)
+ return unpack_meta (path, dest_path, out_checksum, error);
+ else
+ return unpack_file (path, dest_path, out_checksum, error);
+}
+
+
+
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef _OSTREE_CORE
+#define _OSTREE_CORE
+
+#include <otutil.h>
+
+G_BEGIN_DECLS
+
+#define OSTREE_EMPTY_STRING_SHA256 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
+
+typedef enum {
+ OSTREE_OBJECT_TYPE_FILE = 1,
+ OSTREE_OBJECT_TYPE_META = 2,
+} OstreeObjectType;
+
+typedef enum {
+ OSTREE_SERIALIZED_TREE_VARIANT = 1,
+ OSTREE_SERIALIZED_COMMIT_VARIANT = 2,
+ OSTREE_SERIALIZED_DIRMETA_VARIANT = 3,
+ OSTREE_SERIALIZED_XATTR_VARIANT = 4
+} OstreeSerializedVariantType;
+#define OSTREE_SERIALIZED_VARIANT_LAST 4
+
+#define OSTREE_SERIALIZED_VARIANT_FORMAT "(uv)"
+
+/*
+ * xattr objects:
+ * a(ayay) - array of (name, value) pairs, both binary data, though name is a bytestring
+ */
+#define OSTREE_XATTR_GVARIANT_FORMAT "a(ayay)"
+
+#define OSTREE_DIR_META_VERSION 0
+/*
+ * dirmeta objects:
+ * u - Version
+ * u - uid
+ * u - gid
+ * u - mode
+ * a(ayay) - xattrs
+ */
+#define OSTREE_DIRMETA_GVARIANT_FORMAT "(uuuua(ayay))"
+
+#define OSTREE_TREE_VERSION 0
+/*
+ * Tree objects:
+ * u - Version
+ * a{sv} - Metadata
+ * a(ss) - array of (filename, checksum) for files
+ * a(sss) - array of (dirname, tree_checksum, meta_checksum) for directories
+ */
+#define OSTREE_TREE_GVARIANT_FORMAT "(ua{sv}a(ss)a(sss)"
+
+#define OSTREE_COMMIT_VERSION 0
+/*
+ * Commit objects:
+ * u - Version
+ * a{sv} - Metadata
+ * s - parent checksum (empty string for initial)
+ * s - subject
+ * s - body
+ * t - Timestamp in seconds since the epoch (UTC)
+ * s - Root tree contents
+ * s - Root tree metadata
+ */
+#define OSTREE_COMMIT_GVARIANT_FORMAT "(ua{sv}ssstss)"
+
+gboolean ostree_validate_checksum_string (const char *sha256,
+ GError **error);
+
+char *ostree_get_relative_object_path (const char *checksum,
+ OstreeObjectType type,
+ gboolean archive);
+
+GVariant *ostree_get_xattrs_for_path (const char *path,
+ GError **error);
+
+gboolean ostree_set_xattrs (const char *path, GVariant *xattrs, GError **error);
+
+gboolean ostree_parse_metadata_file (const char *path,
+ OstreeSerializedVariantType *out_type,
+ GVariant **out_variant,
+ GError **error);
+
+gboolean ostree_stat_and_checksum_file (int dirfd, const char *path,
+ OstreeObjectType type,
+ GChecksum **out_checksum,
+ struct stat *out_stbuf,
+ GError **error);
+
+/* Packed files:
+ *
+ * guint32 metadata_length [metadata gvariant] [content]
+ *
+ * metadata variant:
+ * u - Version
+ * u - uid
+ * u - gid
+ * u - mode
+ * a(ayay) - xattrs
+ * t - content length
+ *
+ * And then following the end of the variant is the content. If
+ * symlink, then this is the target; if device, then device ID as
+ * network byte order uint32.
+ */
+#define OSTREE_PACK_FILE_VARIANT_FORMAT "(uuuua(ayay)t)"
+
+gboolean ostree_pack_object (GOutputStream *output,
+ GFile *path,
+ OstreeObjectType objtype,
+ GCancellable *cancellable,
+ GError **error);
+
+gboolean ostree_unpack_object (const char *path,
+ OstreeObjectType objtype,
+ const char *dest_path,
+ GChecksum **out_checksum,
+ GError **error);
+
+void ostree_checksum_update_stat (GChecksum *checksum, guint32 uid, guint32 gid, guint32 mode);
+
+
+#endif /* _OSTREE_REPO */
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#define _GNU_SOURCE
+
+#include "config.h"
+
+#include "ostree.h"
+#include "otutil.h"
+
+#include <gio/gunixoutputstream.h>
+#include <gio/gunixinputstream.h>
+
+static gboolean
+link_one_file (OstreeRepo *self, const char *path,
+ OstreeObjectType type,
+ gboolean ignore_exists, gboolean force,
+ GChecksum **out_checksum,
+ GError **error);
+static char *
+get_object_path (OstreeRepo *self,
+ const char *checksum,
+ OstreeObjectType type);
+
+enum {
+ PROP_0,
+
+ PROP_PATH
+};
+
+G_DEFINE_TYPE (OstreeRepo, ostree_repo, G_TYPE_OBJECT)
+
+#define GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), OSTREE_TYPE_REPO, OstreeRepoPrivate))
+
+typedef struct _OstreeRepoPrivate OstreeRepoPrivate;
+
+struct _OstreeRepoPrivate {
+ char *path;
+ GFile *repo_file;
+ GFile *local_heads_dir;
+ GFile *remote_heads_dir;
+ char *objects_path;
+ char *config_path;
+
+ gboolean inited;
+
+ GKeyFile *config;
+ gboolean archive;
+};
+
+static void
+ostree_repo_finalize (GObject *object)
+{
+ OstreeRepo *self = OSTREE_REPO (object);
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+
+ g_free (priv->path);
+ g_clear_object (&priv->repo_file);
+ g_clear_object (&priv->local_heads_dir);
+ g_clear_object (&priv->remote_heads_dir);
+ g_free (priv->objects_path);
+ g_free (priv->config_path);
+ if (priv->config)
+ g_key_file_free (priv->config);
+
+ G_OBJECT_CLASS (ostree_repo_parent_class)->finalize (object);
+}
+
+static void
+ostree_repo_set_property(GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ OstreeRepo *self = OSTREE_REPO (object);
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+
+ switch (prop_id)
+ {
+ case PROP_PATH:
+ priv->path = g_value_dup_string (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+ostree_repo_get_property(GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ OstreeRepo *self = OSTREE_REPO (object);
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+
+ switch (prop_id)
+ {
+ case PROP_PATH:
+ g_value_set_string (value, priv->path);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static GObject *
+ostree_repo_constructor (GType gtype,
+ guint n_properties,
+ GObjectConstructParam *properties)
+{
+ GObject *object;
+ GObjectClass *parent_class;
+ OstreeRepoPrivate *priv;
+
+ parent_class = G_OBJECT_CLASS (ostree_repo_parent_class);
+ object = parent_class->constructor (gtype, n_properties, properties);
+
+ priv = GET_PRIVATE (object);
+
+ g_assert (priv->path != NULL);
+
+ priv->repo_file = ot_util_new_file_for_path (priv->path);
+ priv->local_heads_dir = g_file_resolve_relative_path (priv->repo_file, "refs/heads");
+ priv->remote_heads_dir = g_file_resolve_relative_path (priv->repo_file, "refs/remotes");
+
+ priv->objects_path = g_build_filename (priv->path, "objects", NULL);
+ priv->config_path = g_build_filename (priv->path, "config", NULL);
+
+ return object;
+}
+
+static void
+ostree_repo_class_init (OstreeRepoClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (OstreeRepoPrivate));
+
+ object_class->constructor = ostree_repo_constructor;
+ object_class->get_property = ostree_repo_get_property;
+ object_class->set_property = ostree_repo_set_property;
+ object_class->finalize = ostree_repo_finalize;
+
+ g_object_class_install_property (object_class,
+ PROP_PATH,
+ g_param_spec_string ("path",
+ "",
+ "",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+ostree_repo_init (OstreeRepo *self)
+{
+}
+
+OstreeRepo*
+ostree_repo_new (const char *path)
+{
+ return g_object_new (OSTREE_TYPE_REPO, "path", path, NULL);
+}
+
+static gboolean
+parse_rev_file (OstreeRepo *self,
+ const char *path,
+ char **sha256,
+ GError **error) G_GNUC_UNUSED;
+
+static gboolean
+parse_rev_file (OstreeRepo *self,
+ const char *path,
+ char **sha256,
+ GError **error)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ GError *temp_error = NULL;
+ gboolean ret = FALSE;
+ char *rev = NULL;
+
+ rev = ot_util_get_file_contents_utf8 (path, &temp_error);
+ if (rev == NULL)
+ {
+ if (g_error_matches (temp_error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
+ {
+ g_clear_error (&temp_error);
+ }
+ else
+ {
+ g_propagate_error (error, temp_error);
+ goto out;
+ }
+ }
+ else
+ {
+ g_strchomp (rev);
+ }
+
+ if (g_str_has_prefix (rev, "ref: "))
+ {
+ GFile *ref;
+ char *ref_path;
+ char *ref_sha256;
+ gboolean subret;
+
+ ref = g_file_resolve_relative_path (priv->local_heads_dir, rev + 5);
+ ref_path = g_file_get_path (ref);
+
+ subret = parse_rev_file (self, ref_path, &ref_sha256, error);
+ g_clear_object (&ref);
+ g_free (ref_path);
+
+ if (!subret)
+ {
+ g_free (ref_sha256);
+ goto out;
+ }
+
+ g_free (rev);
+ rev = ref_sha256;
+ }
+ else
+ {
+ if (!ostree_validate_checksum_string (rev, error))
+ goto out;
+ }
+
+ *sha256 = rev;
+ rev = NULL;
+ ret = TRUE;
+ out:
+ g_free (rev);
+ return ret;
+}
+
+static gboolean
+resolve_rev (OstreeRepo *self,
+ const char *rev,
+ gboolean allow_noent,
+ char **sha256,
+ GError **error)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ gboolean ret = FALSE;
+ char *tmp = NULL;
+ char *tmp2 = NULL;
+ char *ret_rev = NULL;
+ GFile *child = NULL;
+ char *child_path = NULL;
+ GError *temp_error = NULL;
+ GVariant *commit = NULL;
+
+ if (strlen (rev) == 0)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Invalid empty rev");
+ goto out;
+ }
+ else if (strlen (rev) == 64)
+ {
+ ret_rev = g_strdup (rev);
+ }
+ else if (g_str_has_suffix (rev, "^"))
+ {
+ tmp = g_strdup (rev);
+ tmp[strlen(tmp) - 1] = '\0';
+
+ if (!resolve_rev (self, tmp, allow_noent, &tmp2, error))
+ goto out;
+
+ if (!ostree_repo_load_variant_checked (self, OSTREE_SERIALIZED_COMMIT_VARIANT, tmp2, &commit, error))
+ goto out;
+
+ g_variant_get_child (commit, 2, "s", &ret_rev);
+ if (strlen (ret_rev) == 0)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Commit %s has no parent", tmp2);
+ goto out;
+
+ }
+ }
+ else
+ {
+ child = g_file_get_child (priv->local_heads_dir, rev);
+ child_path = g_file_get_path (child);
+ if (!ot_util_gfile_load_contents_utf8 (child, NULL, &ret_rev, NULL, &temp_error))
+ {
+ if (allow_noent && g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+ {
+ g_free (ret_rev);
+ ret_rev = NULL;
+ }
+ else
+ {
+ g_propagate_error (error, temp_error);
+ g_prefix_error (error, "Couldn't open ref '%s': ", child_path);
+ goto out;
+ }
+ }
+ else
+ {
+ g_strchomp (ret_rev);
+
+ if (!ostree_validate_checksum_string (ret_rev, error))
+ goto out;
+ }
+ }
+
+ *sha256 = ret_rev;
+ ret_rev = NULL;
+ ret = TRUE;
+ out:
+ if (commit)
+ g_variant_unref (commit);
+ g_free (tmp);
+ g_free (tmp2);
+ g_clear_object (&child);
+ g_free (child_path);
+ g_free (ret_rev);
+ return ret;
+}
+
+gboolean
+ostree_repo_resolve_rev (OstreeRepo *self,
+ const char *rev,
+ char **sha256,
+ GError **error)
+{
+ return resolve_rev (self, rev, FALSE, sha256, error);
+}
+
+static gboolean
+write_checksum_file (GFile *parentdir,
+ const char *name,
+ const char *sha256,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GFile *child = NULL;
+ GOutputStream *out = NULL;
+ gsize bytes_written;
+
+ child = g_file_get_child (parentdir, name);
+
+ if ((out = (GOutputStream*)g_file_replace (child, NULL, FALSE, 0, NULL, error)) == NULL)
+ goto out;
+ if (!g_output_stream_write_all (out, sha256, strlen (sha256), &bytes_written, NULL, error))
+ goto out;
+ if (!g_output_stream_write_all (out, "\n", 1, &bytes_written, NULL, error))
+ goto out;
+ if (!g_output_stream_close (out, NULL, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ g_clear_object (&child);
+ g_clear_object (&out);
+ return ret;
+}
+
+/**
+ * ostree_repo_get_config:
+ * @self:
+ *
+ * Returns: (transfer none): The repository configuration; do not modify
+ */
+GKeyFile *
+ostree_repo_get_config (OstreeRepo *self)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+
+ g_return_val_if_fail (priv->inited, NULL);
+
+ return priv->config;
+}
+
+/**
+ * ostree_repo_copy_config:
+ * @self:
+ *
+ * Returns: (transfer full): A newly-allocated copy of the repository config
+ */
+GKeyFile *
+ostree_repo_copy_config (OstreeRepo *self)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ GKeyFile *copy;
+ char *data;
+ gsize len;
+
+ g_return_val_if_fail (priv->inited, NULL);
+
+ copy = g_key_file_new ();
+ data = g_key_file_to_data (priv->config, &len, NULL);
+ if (!g_key_file_load_from_data (copy, data, len, 0, NULL))
+ g_assert_not_reached ();
+ g_free (data);
+ return copy;
+}
+
+/**
+ * ostree_repo_write_config:
+ * @self:
+ * @new_config: Overwrite the config file with this data. Do not change later!
+ * @error: a #GError
+ *
+ * Save @new_config in place of this repository's config file. Note
+ * that @new_config should not be modified after - this function
+ * simply adds a reference.
+ */
+gboolean
+ostree_repo_write_config (OstreeRepo *self,
+ GKeyFile *new_config,
+ GError **error)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ char *data = NULL;
+ gsize len;
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (priv->inited, FALSE);
+
+ data = g_key_file_to_data (new_config, &len, error);
+ if (!g_file_set_contents (priv->config_path, data, len, error))
+ goto out;
+
+ g_key_file_unref (priv->config);
+ priv->config = g_key_file_ref (new_config);
+
+ ret = TRUE;
+ out:
+ g_free (data);
+ return ret;
+}
+
+gboolean
+ostree_repo_check (OstreeRepo *self, GError **error)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ gboolean ret = FALSE;
+ char *version = NULL;;
+ GError *temp_error = NULL;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ if (priv->inited)
+ return TRUE;
+
+ if (!g_file_test (priv->objects_path, G_FILE_TEST_IS_DIR))
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Couldn't find objects directory '%s'", priv->objects_path);
+ goto out;
+ }
+
+ priv->config = g_key_file_new ();
+ if (!g_key_file_load_from_file (priv->config, priv->config_path, 0, error))
+ {
+ g_prefix_error (error, "Couldn't parse config file: ");
+ goto out;
+ }
+
+ version = g_key_file_get_value (priv->config, "core", "repo_version", &temp_error);
+ if (temp_error)
+ {
+ g_propagate_error (error, temp_error);
+ goto out;
+ }
+
+ if (strcmp (version, "0") != 0)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Invalid repository version '%s'", version);
+ goto out;
+ }
+
+ priv->archive = g_key_file_get_boolean (priv->config, "core", "archive", &temp_error);
+ if (temp_error)
+ {
+ if (g_error_matches (temp_error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND))
+ {
+ g_clear_error (&temp_error);
+ }
+ else
+ {
+ g_propagate_error (error, temp_error);
+ goto out;
+ }
+ }
+
+ priv->inited = TRUE;
+
+ ret = TRUE;
+ out:
+ g_free (version);
+ return ret;
+}
+
+const char *
+ostree_repo_get_path (OstreeRepo *self)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ return priv->path;
+}
+
+gboolean
+ostree_repo_is_archive (OstreeRepo *self)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+
+ g_return_val_if_fail (priv->inited, FALSE);
+
+ return priv->archive;
+}
+
+static gboolean
+import_gvariant_object (OstreeRepo *self,
+ OstreeSerializedVariantType type,
+ GVariant *variant,
+ GChecksum **out_checksum,
+ GError **error)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ GVariant *serialized = NULL;
+ gboolean ret = FALSE;
+ gsize bytes_written;
+ char *tmp_name = NULL;
+ int fd = -1;
+ GUnixOutputStream *stream = NULL;
+
+ serialized = g_variant_new ("(uv)", GUINT32_TO_BE ((guint32)type), variant);
+
+ tmp_name = g_build_filename (priv->objects_path, "variant-tmp-XXXXXX", NULL);
+ fd = g_mkstemp (tmp_name);
+ if (fd < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+
+ stream = (GUnixOutputStream*)g_unix_output_stream_new (fd, FALSE);
+ if (!g_output_stream_write_all ((GOutputStream*)stream,
+ g_variant_get_data (serialized),
+ g_variant_get_size (serialized),
+ &bytes_written,
+ NULL,
+ error))
+ goto out;
+ if (!g_output_stream_close ((GOutputStream*)stream,
+ NULL, error))
+ goto out;
+
+ if (!link_one_file (self, tmp_name, OSTREE_OBJECT_TYPE_META,
+ TRUE, FALSE, out_checksum, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ /* Unconditionally unlink; if we suceeded, there's a new link, if not, clean up. */
+ (void) unlink (tmp_name);
+ if (fd != -1)
+ close (fd);
+ if (serialized != NULL)
+ g_variant_unref (serialized);
+ g_free (tmp_name);
+ g_clear_object (&stream);
+ return ret;
+}
+
+gboolean
+ostree_repo_load_variant_checked (OstreeRepo *self,
+ OstreeSerializedVariantType expected_type,
+ const char *sha256,
+ GVariant **out_variant,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ OstreeSerializedVariantType type;
+ GVariant *ret_variant = NULL;
+
+ if (!ostree_repo_load_variant (self, sha256, &type, &ret_variant, error))
+ goto out;
+
+ if (type != expected_type)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Corrupted metadata object '%s'; found type %u, expected %u", sha256,
+ type, (guint32)expected_type);
+ goto out;
+ }
+
+ ret = TRUE;
+ *out_variant = ret_variant;
+ ret_variant = NULL;
+ out:
+ if (ret_variant)
+ g_variant_unref (ret_variant);
+ return ret;
+}
+
+static gboolean
+import_directory_meta (OstreeRepo *self,
+ const char *path,
+ GVariant **out_variant,
+ GChecksum **out_checksum,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ struct stat stbuf;
+ GChecksum *ret_checksum = NULL;
+ GVariant *dirmeta = NULL;
+ GVariant *xattrs = NULL;
+
+ if (lstat (path, &stbuf) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+
+ if (!S_ISDIR(stbuf.st_mode))
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Not a directory: '%s'", path);
+ goto out;
+ }
+
+ xattrs = ostree_get_xattrs_for_path (path, error);
+ if (!xattrs)
+ goto out;
+
+ dirmeta = g_variant_new ("(uuuu@a(ayay))",
+ OSTREE_DIR_META_VERSION,
+ GUINT32_TO_BE ((guint32)stbuf.st_uid),
+ GUINT32_TO_BE ((guint32)stbuf.st_gid),
+ GUINT32_TO_BE ((guint32)stbuf.st_mode),
+ xattrs);
+ g_variant_ref_sink (dirmeta);
+
+ if (!import_gvariant_object (self, OSTREE_SERIALIZED_DIRMETA_VARIANT,
+ dirmeta, &ret_checksum, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ if (!ret)
+ {
+ if (ret_checksum)
+ g_checksum_free (ret_checksum);
+ if (dirmeta != NULL)
+ g_variant_unref (dirmeta);
+ }
+ else
+ {
+ *out_checksum = ret_checksum;
+ *out_variant = dirmeta;
+ }
+ if (xattrs)
+ g_variant_unref (xattrs);
+ return ret;
+}
+
+static char *
+get_object_path (OstreeRepo *self,
+ const char *checksum,
+ OstreeObjectType type)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ char *ret;
+ char *relpath;
+
+ relpath = ostree_get_relative_object_path (checksum, type, priv->archive);
+ ret = g_build_filename (priv->path, relpath, NULL);
+ g_free (relpath);
+
+ return ret;
+}
+
+static char *
+prepare_dir_for_checksum_get_object_path (OstreeRepo *self,
+ const char *checksum,
+ OstreeObjectType type,
+ GError **error)
+{
+ char *checksum_dir = NULL;
+ char *object_path = NULL;
+
+ object_path = get_object_path (self, checksum, type);
+ checksum_dir = g_path_get_dirname (object_path);
+
+ if (!ot_util_ensure_directory (checksum_dir, FALSE, error))
+ goto out;
+
+ out:
+ g_free (checksum_dir);
+ return object_path;
+}
+
+static gboolean
+link_object_trusted (OstreeRepo *self,
+ const char *path,
+ const char *checksum,
+ OstreeObjectType objtype,
+ gboolean ignore_exists,
+ gboolean force,
+ gboolean *did_exist,
+ GError **error)
+{
+ char *src_basename = NULL;
+ char *src_dirname = NULL;
+ char *dest_basename = NULL;
+ char *tmp_dest_basename = NULL;
+ char *dest_dirname = NULL;
+ DIR *src_dir = NULL;
+ DIR *dest_dir = NULL;
+ gboolean ret = FALSE;
+ char *dest_path = NULL;
+
+ src_basename = g_path_get_basename (path);
+ src_dirname = g_path_get_dirname (path);
+
+ src_dir = opendir (src_dirname);
+ if (src_dir == NULL)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+
+ dest_path = prepare_dir_for_checksum_get_object_path (self, checksum, objtype, error);
+ if (!dest_path)
+ goto out;
+
+ dest_basename = g_path_get_basename (dest_path);
+ dest_dirname = g_path_get_dirname (dest_path);
+ dest_dir = opendir (dest_dirname);
+ if (dest_dir == NULL)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+
+ if (force)
+ {
+ tmp_dest_basename = g_strconcat (dest_basename, ".tmp", NULL);
+ (void) unlinkat (dirfd (dest_dir), tmp_dest_basename, 0);
+ }
+ else
+ tmp_dest_basename = g_strdup (dest_basename);
+
+ if (linkat (dirfd (src_dir), src_basename, dirfd (dest_dir), tmp_dest_basename, 0) < 0)
+ {
+ if (errno != EEXIST || !ignore_exists)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ else
+ *did_exist = TRUE;
+ }
+ else
+ *did_exist = FALSE;
+
+ if (force)
+ {
+ if (renameat (dirfd (dest_dir), tmp_dest_basename,
+ dirfd (dest_dir), dest_basename) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ (void) unlinkat (dirfd (dest_dir), tmp_dest_basename, 0);
+ }
+
+ ret = TRUE;
+ out:
+ if (src_dir != NULL)
+ closedir (src_dir);
+ if (dest_dir != NULL)
+ closedir (dest_dir);
+ g_free (src_basename);
+ g_free (src_dirname);
+ g_free (dest_basename);
+ g_free (tmp_dest_basename);
+ g_free (dest_dirname);
+ g_free (dest_path);
+ return ret;
+}
+
+static gboolean
+archive_file_trusted (OstreeRepo *self,
+ const char *path,
+ const char *checksum,
+ OstreeObjectType objtype,
+ gboolean ignore_exists,
+ gboolean force,
+ gboolean *did_exist,
+ GError **error)
+{
+ GFile *infile = NULL;
+ GFile *outfile = NULL;
+ GFileOutputStream *out = NULL;
+ gboolean ret = FALSE;
+ char *dest_path = NULL;
+ char *dest_tmp_path = NULL;
+
+ infile = ot_util_new_file_for_path (path);
+
+ dest_path = prepare_dir_for_checksum_get_object_path (self, checksum, objtype, error);
+ if (!dest_path)
+ goto out;
+
+ dest_tmp_path = g_strconcat (dest_path, ".tmp", NULL);
+
+ outfile = ot_util_new_file_for_path (dest_tmp_path);
+ out = g_file_replace (outfile, NULL, FALSE, 0, NULL, error);
+ if (!out)
+ goto out;
+
+ if (!ostree_pack_object ((GOutputStream*)out, infile, objtype, NULL, error))
+ goto out;
+
+ if (!g_output_stream_close ((GOutputStream*)out, NULL, error))
+ goto out;
+
+ if (rename (dest_tmp_path, dest_path) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+
+ ret = TRUE;
+ out:
+ g_free (dest_path);
+ g_free (dest_tmp_path);
+ g_clear_object (&infile);
+ g_clear_object (&outfile);
+ g_clear_object (&out);
+ return ret;
+}
+
+gboolean
+ostree_repo_store_object_trusted (OstreeRepo *self,
+ const char *path,
+ const char *checksum,
+ OstreeObjectType objtype,
+ gboolean ignore_exists,
+ gboolean force,
+ gboolean *did_exist,
+ GError **error)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ if (priv->archive && objtype == OSTREE_OBJECT_TYPE_FILE)
+ return archive_file_trusted (self, path, checksum, objtype, ignore_exists, force, did_exist, error);
+ else
+ return link_object_trusted (self, path, checksum, objtype, ignore_exists, force, did_exist, error);
+}
+
+static gboolean
+link_one_file (OstreeRepo *self, const char *path, OstreeObjectType type,
+ gboolean ignore_exists, gboolean force,
+ GChecksum **out_checksum,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ struct stat stbuf;
+ GChecksum *id = NULL;
+ gboolean did_exist;
+
+ if (!ostree_stat_and_checksum_file (-1, path, type, &id, &stbuf, error))
+ goto out;
+
+ if (!ostree_repo_store_object_trusted (self, path, g_checksum_get_string (id), type,
+ ignore_exists, force, &did_exist, error))
+ goto out;
+
+ *out_checksum = id;
+ id = NULL;
+ ret = TRUE;
+ out:
+ if (id != NULL)
+ g_checksum_free (id);
+ return ret;
+}
+
+gboolean
+ostree_repo_link_file (OstreeRepo *self,
+ const char *path,
+ gboolean ignore_exists,
+ gboolean force,
+ GError **error)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ GChecksum *checksum = NULL;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+ g_return_val_if_fail (priv->inited, FALSE);
+
+ if (!link_one_file (self, path, OSTREE_OBJECT_TYPE_FILE,
+ ignore_exists, force, &checksum, error))
+ return FALSE;
+ g_checksum_free (checksum);
+ return TRUE;
+}
+
+gboolean
+ostree_repo_store_packfile (OstreeRepo *self,
+ const char *expected_checksum,
+ const char *path,
+ OstreeObjectType objtype,
+ GError **error)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ gboolean ret = FALSE;
+ GString *tempfile_path = NULL;
+ GChecksum *checksum = NULL;
+ struct stat stbuf;
+ gboolean did_exist;
+
+ tempfile_path = g_string_new (priv->path);
+ g_string_append_printf (tempfile_path, "/tmp-unpack-%s", expected_checksum);
+
+ if (!ostree_unpack_object (path, objtype, tempfile_path->str, &checksum, error))
+ goto out;
+
+ if (strcmp (g_checksum_get_string (checksum), expected_checksum) != 0)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Corrupted object %s (actual checksum is %s)",
+ expected_checksum, g_checksum_get_string (checksum));
+ goto out;
+ }
+
+ if (!ostree_repo_store_object_trusted (self, tempfile_path ? tempfile_path->str : path,
+ expected_checksum,
+ objtype,
+ TRUE, FALSE, &did_exist, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ if (tempfile_path)
+ {
+ (void) unlink (tempfile_path->str);
+ g_string_free (tempfile_path, TRUE);
+ }
+ if (checksum)
+ g_checksum_free (checksum);
+ return ret;
+}
+
+typedef struct _ParsedTreeData ParsedTreeData;
+typedef struct _ParsedDirectoryData ParsedDirectoryData;
+
+static void parsed_tree_data_free (ParsedTreeData *pdata);
+
+struct _ParsedDirectoryData {
+ ParsedTreeData *tree_data;
+ char *metadata_sha256;
+ GVariant *meta_data;
+};
+
+static void
+parsed_directory_data_free (ParsedDirectoryData *pdata)
+{
+ if (pdata == NULL)
+ return;
+ parsed_tree_data_free (pdata->tree_data);
+ g_free (pdata->metadata_sha256);
+ g_variant_unref (pdata->meta_data);
+ g_free (pdata);
+}
+
+struct _ParsedTreeData {
+ GHashTable *files; /* char* filename -> char* checksum */
+ GHashTable *directories; /* char* dirname -> ParsedDirectoryData* */
+};
+
+static ParsedTreeData *
+parsed_tree_data_new (void)
+{
+ ParsedTreeData *ret = g_new0 (ParsedTreeData, 1);
+ ret->files = g_hash_table_new_full (g_str_hash, g_str_equal,
+ (GDestroyNotify)g_free,
+ (GDestroyNotify)g_free);
+ ret->directories = g_hash_table_new_full (g_str_hash, g_str_equal,
+ (GDestroyNotify)g_free,
+ (GDestroyNotify)parsed_directory_data_free);
+ return ret;
+}
+
+static void
+parsed_tree_data_free (ParsedTreeData *pdata)
+{
+ if (pdata == NULL)
+ return;
+ g_hash_table_destroy (pdata->files);
+ g_hash_table_destroy (pdata->directories);
+ g_free (pdata);
+}
+
+static gboolean
+parse_tree (OstreeRepo *self,
+ const char *sha256,
+ ParsedTreeData **out_pdata,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ ParsedTreeData *ret_pdata = NULL;
+ int i, n;
+ guint32 version;
+ GVariant *tree_variant = NULL;
+ GVariant *meta_variant = NULL;
+ GVariant *files_variant = NULL;
+ GVariant *dirs_variant = NULL;
+
+ if (!ostree_repo_load_variant_checked (self, OSTREE_SERIALIZED_TREE_VARIANT,
+ sha256, &tree_variant, error))
+ goto out;
+
+ /* PARSE OSTREE_SERIALIZED_TREE_VARIANT */
+ g_variant_get (tree_variant, "(u@a{sv}@a(ss)@a(sss))",
+ &version, &meta_variant, &files_variant, &dirs_variant);
+ version = GUINT32_FROM_BE (version);
+
+ ret_pdata = parsed_tree_data_new ();
+ n = g_variant_n_children (files_variant);
+ for (i = 0; i < n; i++)
+ {
+ const char *filename;
+ const char *checksum;
+
+ g_variant_get_child (files_variant, i, "(&s&s)", &filename, &checksum);
+
+ g_hash_table_insert (ret_pdata->files, g_strdup (filename), g_strdup (checksum));
+ }
+
+ n = g_variant_n_children (dirs_variant);
+ for (i = 0; i < n; i++)
+ {
+ const char *dirname;
+ const char *tree_checksum;
+ const char *meta_checksum;
+ ParsedTreeData *child_tree = NULL;
+ GVariant *metadata = NULL;
+ ParsedDirectoryData *child_dir = NULL;
+
+ g_variant_get_child (dirs_variant, i, "(&s&s&s)",
+ &dirname, &tree_checksum, &meta_checksum);
+
+ if (!parse_tree (self, tree_checksum, &child_tree, error))
+ goto out;
+
+ if (!ostree_repo_load_variant_checked (self, OSTREE_SERIALIZED_DIRMETA_VARIANT,
+ meta_checksum, &metadata, error))
+ {
+ parsed_tree_data_free (child_tree);
+ goto out;
+ }
+
+ child_dir = g_new0 (ParsedDirectoryData, 1);
+ child_dir->tree_data = child_tree;
+ child_dir->metadata_sha256 = g_strdup (meta_checksum);
+ child_dir->meta_data = metadata;
+
+ g_hash_table_insert (ret_pdata->directories, g_strdup (dirname), child_dir);
+ }
+
+ ret = TRUE;
+ out:
+ if (!ret)
+ parsed_tree_data_free (ret_pdata);
+ else
+ *out_pdata = ret_pdata;
+ if (tree_variant)
+ g_variant_unref (tree_variant);
+ if (meta_variant)
+ g_variant_unref (meta_variant);
+ if (files_variant)
+ g_variant_unref (files_variant);
+ if (dirs_variant)
+ g_variant_unref (dirs_variant);
+ return ret;
+}
+
+static gboolean
+load_commit_and_trees (OstreeRepo *self,
+ const char *commit_sha256,
+ GVariant **out_commit,
+ ParsedDirectoryData **out_root_data,
+ GError **error)
+{
+ GVariant *ret_commit = NULL;
+ ParsedDirectoryData *ret_root_data = NULL;
+ ParsedTreeData *tree_data = NULL;
+ char *ret_metadata_checksum = NULL;
+ GVariant *root_metadata = NULL;
+ gboolean ret = FALSE;
+ const char *tree_contents_checksum;
+ const char *tree_meta_checksum;
+
+ if (!ostree_repo_load_variant_checked (self, OSTREE_SERIALIZED_COMMIT_VARIANT,
+ commit_sha256, &ret_commit, error))
+ goto out;
+
+ /* PARSE OSTREE_SERIALIZED_COMMIT_VARIANT */
+ g_variant_get_child (ret_commit, 6, "&s", &tree_contents_checksum);
+ g_variant_get_child (ret_commit, 7, "&s", &tree_meta_checksum);
+
+ if (!ostree_repo_load_variant_checked (self, OSTREE_SERIALIZED_DIRMETA_VARIANT,
+ tree_meta_checksum, &root_metadata, error))
+ goto out;
+
+ if (!parse_tree (self, tree_contents_checksum, &tree_data, error))
+ goto out;
+
+ ret_root_data = g_new0 (ParsedDirectoryData, 1);
+ ret_root_data->tree_data = tree_data;
+ ret_root_data->metadata_sha256 = g_strdup (tree_meta_checksum);
+ ret_root_data->meta_data = root_metadata;
+ root_metadata = NULL;
+
+ ret = TRUE;
+ *out_commit = ret_commit;
+ ret_commit = NULL;
+ *out_root_data = ret_root_data;
+ ret_root_data = NULL;
+ out:
+ if (ret_commit)
+ g_variant_unref (ret_commit);
+ parsed_directory_data_free (ret_root_data);
+ g_free (ret_metadata_checksum);
+ if (root_metadata)
+ g_variant_unref (root_metadata);
+ return ret;
+}
+
+static GVariant *
+create_empty_gvariant_dict (void)
+{
+ GVariantBuilder builder;
+ g_variant_builder_init (&builder, G_VARIANT_TYPE("a{sv}"));
+ return g_variant_builder_end (&builder);
+}
+
+static gboolean
+import_parsed_tree (OstreeRepo *self,
+ ParsedTreeData *tree,
+ GChecksum **out_checksum,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GVariant *serialized_tree = NULL;
+ gboolean builders_initialized = FALSE;
+ GVariantBuilder files_builder;
+ GVariantBuilder dirs_builder;
+ GHashTableIter hash_iter;
+ gpointer key, value;
+
+ g_variant_builder_init (&files_builder, G_VARIANT_TYPE ("a(ss)"));
+ g_variant_builder_init (&dirs_builder, G_VARIANT_TYPE ("a(sss)"));
+ builders_initialized = TRUE;
+
+ g_hash_table_iter_init (&hash_iter, tree->files);
+ while (g_hash_table_iter_next (&hash_iter, &key, &value))
+ {
+ const char *name = key;
+ const char *checksum = value;
+
+ g_variant_builder_add (&files_builder, "(ss)", name, checksum);
+ }
+
+ g_hash_table_iter_init (&hash_iter, tree->directories);
+ while (g_hash_table_iter_next (&hash_iter, &key, &value))
+ {
+ const char *name = key;
+ GChecksum *dir_checksum = NULL;
+ ParsedDirectoryData *dir = value;
+
+ if (!import_parsed_tree (self, dir->tree_data, &dir_checksum, error))
+ goto out;
+
+ g_variant_builder_add (&dirs_builder, "(sss)",
+ name, g_checksum_get_string (dir_checksum), dir->metadata_sha256);
+ g_checksum_free (dir_checksum);
+ }
+
+ serialized_tree = g_variant_new ("(u@a{sv}@a(ss)@a(sss))",
+ GUINT32_TO_BE (0),
+ create_empty_gvariant_dict (),
+ g_variant_builder_end (&files_builder),
+ g_variant_builder_end (&dirs_builder));
+ builders_initialized = FALSE;
+ g_variant_ref_sink (serialized_tree);
+ if (!import_gvariant_object (self, OSTREE_SERIALIZED_TREE_VARIANT, serialized_tree, out_checksum, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ if (builders_initialized)
+ {
+ g_variant_builder_clear (&files_builder);
+ g_variant_builder_clear (&dirs_builder);
+ }
+ if (serialized_tree)
+ g_variant_unref (serialized_tree);
+ return ret;
+}
+
+static gboolean
+check_path (const char *filename,
+ GError **error)
+{
+ gboolean ret = FALSE;
+
+ if (!*filename)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Invalid empty filename");
+ goto out;
+ }
+
+ if (strcmp (filename, ".") == 0)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Self-reference '.' in filename '%s' not allowed (yet)", filename);
+ goto out;
+ }
+
+ if (ot_util_filename_has_dotdot (filename))
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Path uplink '..' in filename '%s' not allowed (yet)", filename);
+ goto out;
+ }
+
+ if (g_path_is_absolute (filename))
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Absolute filename '%s' not allowed (yet)", filename);
+ goto out;
+ }
+
+ ret = TRUE;
+ out:
+ return ret;
+}
+
+static gboolean
+walk_parsed_tree (OstreeRepo *self,
+ const char *filename,
+ ParsedTreeData *tree,
+ int *out_filename_index, /* out*/
+ char **out_component, /* out, must free */
+ ParsedTreeData **out_tree, /* out, but do not free */
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GPtrArray *components = NULL;
+ ParsedTreeData *current_tree = tree;
+ const char *component = NULL;
+ const char *file_sha1 = NULL;
+ ParsedDirectoryData *dir = NULL;
+ int i;
+ int ret_filename_index = 0;
+
+ components = ot_util_path_split (filename);
+ g_assert (components != NULL);
+
+ current_tree = tree;
+ for (i = 0; i < components->len - 1; i++)
+ {
+ component = components->pdata[i];
+ file_sha1 = g_hash_table_lookup (current_tree->files, component);
+ dir = g_hash_table_lookup (current_tree->directories, component);
+
+ if (!(file_sha1 || dir))
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "No such file or directory: %s",
+ filename);
+ goto out;
+ }
+ else if (file_sha1)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Encountered non-directory '%s' in '%s'",
+ (char*)component,
+ filename);
+ goto out;
+ }
+ else
+ {
+ g_assert (dir != NULL);
+ current_tree = dir->tree_data;
+ ret_filename_index++;
+ }
+ }
+
+ ret = TRUE;
+ *out_filename_index = i;
+ *out_component = components->pdata[components->len-1];
+ components->pdata[components->len-1] = NULL; /* steal */
+ *out_tree = current_tree;
+ out:
+ g_ptr_array_free (components, TRUE);
+ return ret;
+}
+
+static gboolean
+remove_files_from_tree (OstreeRepo *self,
+ const char *base,
+ GPtrArray *removed_files,
+ ParsedTreeData *tree,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ int i;
+
+ for (i = 0; i < removed_files->len; i++)
+ {
+ const char *filename = removed_files->pdata[i];
+ int filename_index;
+ char *component = NULL;
+ ParsedTreeData *parent;
+ const char *file_sha1;
+ ParsedTreeData *dir;
+
+ if (!check_path (filename, error))
+ goto out;
+
+ if (!walk_parsed_tree (self, filename, tree,
+ &filename_index, (char**)&component, &parent,
+ error))
+ goto out;
+
+ file_sha1 = g_hash_table_lookup (parent->files, component);
+ dir = g_hash_table_lookup (parent->directories, component);
+
+ if (file_sha1)
+ g_hash_table_remove (parent->files, component);
+ else if (dir)
+ g_hash_table_remove (parent->directories, component);
+ else
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "No such file or directory: %s",
+ filename);
+ g_free (component);
+ goto out;
+ }
+ g_free (component);
+ }
+
+ ret = TRUE;
+ out:
+ return ret;
+}
+
+static gboolean
+add_one_directory_to_tree_and_import (OstreeRepo *self,
+ const char *basename,
+ const char *abspath,
+ ParsedTreeData *tree,
+ ParsedDirectoryData **dir, /*inout*/
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GVariant *dirmeta = NULL;
+ GChecksum *dir_meta_checksum = NULL;
+ ParsedDirectoryData *dir_value = *dir;
+
+ g_assert (tree != NULL);
+
+ if (!import_directory_meta (self, abspath, &dirmeta, &dir_meta_checksum, error))
+ goto out;
+
+ if (dir_value)
+ {
+ g_variant_unref (dir_value->meta_data);
+ dir_value->meta_data = dirmeta;
+ }
+ else
+ {
+ dir_value = g_new0 (ParsedDirectoryData, 1);
+ dir_value->tree_data = parsed_tree_data_new ();
+ dir_value->metadata_sha256 = g_strdup (g_checksum_get_string (dir_meta_checksum));
+ dir_value->meta_data = dirmeta;
+ g_hash_table_insert (tree->directories, g_strdup (basename), dir_value);
+ }
+
+ ret = TRUE;
+ *dir = dir_value;
+ out:
+ if (dir_meta_checksum)
+ g_checksum_free (dir_meta_checksum);
+ return ret;
+}
+
+static gboolean
+add_one_file_to_tree_and_import (OstreeRepo *self,
+ const char *basename,
+ const char *abspath,
+ ParsedTreeData *tree,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GChecksum *checksum = NULL;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+ g_assert (tree != NULL);
+
+ if (!link_one_file (self, abspath, OSTREE_OBJECT_TYPE_FILE,
+ TRUE, FALSE, &checksum, error))
+ goto out;
+
+ g_hash_table_replace (tree->files, g_strdup (basename),
+ g_strdup (g_checksum_get_string (checksum)));
+
+ ret = TRUE;
+ out:
+ if (checksum)
+ g_checksum_free (checksum);
+ return ret;
+}
+
+static gboolean
+add_one_path_to_tree_and_import (OstreeRepo *self,
+ const char *base,
+ const char *filename,
+ ParsedTreeData *tree,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GPtrArray *components = NULL;
+ struct stat stbuf;
+ char *component_abspath = NULL;
+ ParsedTreeData *current_tree = tree;
+ const char *component = NULL;
+ const char *file_sha1;
+ char *abspath = NULL;
+ ParsedDirectoryData *dir;
+ int i;
+ gboolean is_directory;
+
+ if (!check_path (filename, error))
+ goto out;
+
+ abspath = g_build_filename (base, filename, NULL);
+
+ if (lstat (abspath, &stbuf) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ is_directory = S_ISDIR(stbuf.st_mode);
+
+ if (components)
+ g_ptr_array_free (components, TRUE);
+ components = ot_util_path_split (filename);
+ g_assert (components->len > 0);
+
+ current_tree = tree;
+ for (i = 0; i < components->len; i++)
+ {
+ component = components->pdata[i];
+ g_free (component_abspath);
+ component_abspath = ot_util_path_join_n (base, components, i);
+ file_sha1 = g_hash_table_lookup (current_tree->files, component);
+ dir = g_hash_table_lookup (current_tree->directories, component);
+
+ g_assert_cmpstr (component, !=, ".");
+
+ if (i < components->len - 1)
+ {
+ if (file_sha1 != NULL)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Encountered non-directory '%s' in '%s'",
+ component,
+ filename);
+ goto out;
+ }
+ /* Implicitly add intermediate directories */
+ if (!add_one_directory_to_tree_and_import (self, component,
+ component_abspath, current_tree, &dir,
+ error))
+ goto out;
+ g_assert (dir != NULL);
+ current_tree = dir->tree_data;
+ }
+ else if (is_directory)
+ {
+ if (file_sha1 != NULL)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "File '%s' can't be overwritten by directory",
+ filename);
+ goto out;
+ }
+ if (!add_one_directory_to_tree_and_import (self, component,
+ abspath, current_tree, &dir,
+ error))
+ goto out;
+ }
+ else
+ {
+ g_assert (!is_directory);
+ if (dir != NULL)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "File '%s' can't be overwritten by directory",
+ filename);
+ goto out;
+ }
+ if (!add_one_file_to_tree_and_import (self, component, abspath,
+ current_tree, error))
+ goto out;
+ }
+ }
+
+ ret = TRUE;
+ out:
+ if (components)
+ g_ptr_array_unref (components);
+ g_free (component_abspath);
+ g_free (abspath);
+ return ret;
+}
+
+static gboolean
+add_files_to_tree_and_import (OstreeRepo *self,
+ const char *base,
+ GPtrArray *added_files,
+ ParsedTreeData *tree,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ int i;
+
+ for (i = 0; i < added_files->len; i++)
+ {
+ const char *path = added_files->pdata[i];
+
+ if (!add_one_path_to_tree_and_import (self, base, path, tree, error))
+ goto out;
+ }
+
+ ret = TRUE;
+ out:
+ return ret;
+}
+
+gboolean
+ostree_repo_write_ref (OstreeRepo *self,
+ gboolean is_local,
+ const char *name,
+ const char *rev,
+ GError **error)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ return write_checksum_file (is_local ? priv->local_heads_dir : priv->remote_heads_dir,
+ name, rev, error);
+}
+
+static gboolean
+commit_parsed_tree (OstreeRepo *self,
+ const char *branch,
+ const char *parent,
+ const char *subject,
+ const char *body,
+ GVariant *metadata,
+ ParsedDirectoryData *root,
+ GChecksum **out_commit,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GChecksum *root_checksum = NULL;
+ GChecksum *ret_commit = NULL;
+ GVariant *commit = NULL;
+ GDateTime *now = NULL;
+
+ g_assert (branch != NULL);
+ g_assert (subject != NULL);
+
+ if (!import_parsed_tree (self, root->tree_data, &root_checksum, error))
+ goto out;
+
+ now = g_date_time_new_now_utc ();
+ commit = g_variant_new ("(u@a{sv}ssstss)",
+ GUINT32_TO_BE (OSTREE_COMMIT_VERSION),
+ create_empty_gvariant_dict (),
+ parent ? parent : "",
+ subject, body ? body : "",
+ GUINT64_TO_BE (g_date_time_to_unix (now)),
+ g_checksum_get_string (root_checksum),
+ root->metadata_sha256);
+ g_variant_ref_sink (commit);
+ if (!import_gvariant_object (self, OSTREE_SERIALIZED_COMMIT_VARIANT,
+ commit, &ret_commit, error))
+ goto out;
+
+ if (!ostree_repo_write_ref (self, TRUE, branch, g_checksum_get_string (ret_commit), error))
+ goto out;
+
+ ret = TRUE;
+ *out_commit = ret_commit;
+ out:
+ if (root_checksum)
+ g_checksum_free (root_checksum);
+ if (commit)
+ g_variant_unref (commit);
+ if (now)
+ g_date_time_unref (now);
+ return ret;
+}
+
+static gboolean
+import_root (OstreeRepo *self,
+ const char *base,
+ ParsedDirectoryData **out_root,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ ParsedDirectoryData *ret_root = NULL;
+ GVariant *root_metadata = NULL;
+ GChecksum *root_meta_checksum = NULL;
+
+ if (!import_directory_meta (self, base, &root_metadata, &root_meta_checksum, error))
+ goto out;
+
+ ret_root = g_new0 (ParsedDirectoryData, 1);
+ ret_root->tree_data = parsed_tree_data_new ();
+ ret_root->meta_data = root_metadata;
+ root_metadata = NULL;
+ ret_root->metadata_sha256 = g_strdup (g_checksum_get_string (root_meta_checksum));
+
+ ret = TRUE;
+ *out_root = ret_root;
+ ret_root = NULL;
+ out:
+ if (root_metadata)
+ g_variant_unref (root_metadata);
+ if (root_meta_checksum)
+ g_checksum_free (root_meta_checksum);
+ parsed_directory_data_free (ret_root);
+ return ret;
+}
+
+gboolean
+ostree_repo_commit (OstreeRepo *self,
+ const char *branch,
+ const char *parent,
+ const char *subject,
+ const char *body,
+ GVariant *metadata,
+ const char *base,
+ GPtrArray *modified_files,
+ GPtrArray *removed_files,
+ GChecksum **out_commit,
+ GError **error)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ gboolean ret = FALSE;
+ ParsedDirectoryData *root = NULL;
+ GVariant *previous_commit = NULL;
+ GChecksum *ret_commit_checksum = NULL;
+ GVariant *root_metadata = NULL;
+ GChecksum *root_meta_checksum = NULL;
+ char *current_head = NULL;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+ g_return_val_if_fail (priv->inited, FALSE);
+ g_return_val_if_fail (branch != NULL, FALSE);
+ g_return_val_if_fail (subject != NULL, FALSE);
+
+ if (parent == NULL)
+ parent = branch;
+
+ if (!resolve_rev (self, parent, TRUE, ¤t_head, error))
+ goto out;
+
+ if (current_head)
+ {
+ if (!load_commit_and_trees (self, current_head, &previous_commit, &root, error))
+ goto out;
+ if (!import_directory_meta (self, base, &root_metadata, &root_meta_checksum, error))
+ goto out;
+ g_variant_unref (root->meta_data);
+ root->meta_data = root_metadata;
+ root_metadata = NULL;
+ root->metadata_sha256 = g_strdup (g_checksum_get_string (root_meta_checksum));
+ }
+ else
+ {
+ /* Initial commit */
+ if (!import_root (self, base, &root, error))
+ goto out;
+ }
+
+ if (!remove_files_from_tree (self, base, removed_files, root->tree_data, error))
+ goto out;
+
+ if (!add_files_to_tree_and_import (self, base, modified_files, root->tree_data, error))
+ goto out;
+
+ if (!commit_parsed_tree (self, branch, current_head,
+ subject, body, metadata, root,
+ &ret_commit_checksum, error))
+ goto out;
+
+ ret = TRUE;
+ *out_commit = ret_commit_checksum;
+ ret_commit_checksum = NULL;
+ out:
+ if (ret_commit_checksum)
+ g_checksum_free (ret_commit_checksum);
+ g_free (current_head);
+ if (previous_commit)
+ g_variant_unref (previous_commit);
+ parsed_directory_data_free (root);
+ if (root_metadata)
+ g_variant_unref (root_metadata);
+ if (root_meta_checksum)
+ g_checksum_free (root_meta_checksum);
+ return ret;
+}
+
+gboolean
+ostree_repo_commit_from_filelist_fd (OstreeRepo *self,
+ const char *branch,
+ const char *parent,
+ const char *subject,
+ const char *body,
+ GVariant *metadata,
+ const char *base,
+ int fd,
+ char separator,
+ GChecksum **out_commit,
+ GError **error)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ gboolean ret = FALSE;
+ ParsedDirectoryData *root = NULL;
+ GChecksum *ret_commit_checksum = NULL;
+ GUnixInputStream *in = NULL;
+ GDataInputStream *datain = NULL;
+ char *filename = NULL;
+ gsize filename_len;
+ GError *temp_error = NULL;
+ GVariant *root_metadata = NULL;
+ GChecksum *root_meta_checksum = NULL;
+ char *current_head = NULL;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+ g_return_val_if_fail (priv->inited, FALSE);
+ g_return_val_if_fail (branch != NULL, FALSE);
+ g_return_val_if_fail (subject != NULL, FALSE);
+
+ if (parent == NULL)
+ parent = branch;
+
+ /* We're overwriting the tree */
+ if (!import_root (self, base, &root, error))
+ goto out;
+
+ if (!resolve_rev (self, parent, TRUE, ¤t_head, error))
+ goto out;
+
+ in = (GUnixInputStream*)g_unix_input_stream_new (fd, FALSE);
+ datain = g_data_input_stream_new ((GInputStream*)in);
+
+ while ((filename = g_data_input_stream_read_upto (datain, &separator, 1,
+ &filename_len, NULL, &temp_error)) != NULL)
+ {
+ if (!g_data_input_stream_read_byte (datain, NULL, &temp_error))
+ {
+ if (temp_error != NULL)
+ {
+ g_propagate_prefixed_error (error, temp_error, "%s", "While reading filelist: ");
+ goto out;
+ }
+ }
+ if (!add_one_path_to_tree_and_import (self, base, filename, root->tree_data, error))
+ goto out;
+ g_free (filename);
+ filename = NULL;
+ }
+ if (filename == NULL && temp_error != NULL)
+ {
+ g_propagate_prefixed_error (error, temp_error, "%s", "While reading filelist: ");
+ goto out;
+ }
+ if (!commit_parsed_tree (self, branch, current_head, subject, body, metadata,
+ root, &ret_commit_checksum, error))
+ goto out;
+
+ ret = TRUE;
+ *out_commit = ret_commit_checksum;
+ ret_commit_checksum = NULL;
+ out:
+ if (ret_commit_checksum)
+ g_checksum_free (ret_commit_checksum);
+ g_free (current_head);
+ if (root_metadata)
+ g_variant_unref (root_metadata);
+ if (root_meta_checksum)
+ g_checksum_free (root_meta_checksum);
+ g_clear_object (&datain);
+ g_clear_object (&in);
+ g_free (filename);
+ parsed_directory_data_free (root);
+ return ret;
+
+}
+
+static gboolean
+iter_object_dir (OstreeRepo *self,
+ GFile *dir,
+ OstreeRepoObjectIter callback,
+ gpointer user_data,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GError *temp_error = NULL;
+ GFileEnumerator *enumerator = NULL;
+ GFileInfo *file_info = NULL;
+ char *dirpath = NULL;
+
+ dirpath = g_file_get_path (dir);
+
+ enumerator = g_file_enumerate_children (dir, "standard::name,standard::type,unix::*",
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ NULL,
+ error);
+ if (!enumerator)
+ goto out;
+
+ while ((file_info = g_file_enumerator_next_file (enumerator, NULL, &temp_error)) != NULL)
+ {
+ const char *name;
+ guint32 type;
+ name = g_file_info_get_attribute_byte_string (file_info, "standard::name");
+ type = g_file_info_get_attribute_uint32 (file_info, "standard::type");
+
+ if (type != G_FILE_TYPE_DIRECTORY
+ && (g_str_has_suffix (name, ".meta")
+ || g_str_has_suffix (name, ".file")
+ || g_str_has_suffix (name, ".packfile")))
+ {
+ char *dot;
+ char *path;
+
+ dot = strrchr (name, '.');
+ g_assert (dot);
+
+ if ((dot - name) == 62)
+ {
+ path = g_build_filename (dirpath, name, NULL);
+ callback (self, path, file_info, user_data);
+ g_free (path);
+ }
+ }
+
+ g_object_unref (file_info);
+ }
+ if (file_info == NULL && temp_error != NULL)
+ {
+ g_propagate_error (error, temp_error);
+ goto out;
+ }
+ if (!g_file_enumerator_close (enumerator, NULL, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ g_free (dirpath);
+ return ret;
+}
+
+gboolean
+ostree_repo_iter_objects (OstreeRepo *self,
+ OstreeRepoObjectIter callback,
+ gpointer user_data,
+ GError **error)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ GFile *objectdir = NULL;
+ GFileEnumerator *enumerator = NULL;
+ gboolean ret = FALSE;
+ GFileInfo *file_info = NULL;
+ GError *temp_error = NULL;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+ g_return_val_if_fail (priv->inited, FALSE);
+
+ objectdir = ot_util_new_file_for_path (priv->objects_path);
+ enumerator = g_file_enumerate_children (objectdir, "standard::name,standard::type,unix::*",
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ NULL,
+ error);
+ if (!enumerator)
+ goto out;
+
+ while ((file_info = g_file_enumerator_next_file (enumerator, NULL, &temp_error)) != NULL)
+ {
+ const char *name;
+ guint32 type;
+
+ name = g_file_info_get_attribute_byte_string (file_info, "standard::name");
+ type = g_file_info_get_attribute_uint32 (file_info, "standard::type");
+
+ if (strlen (name) == 2 && type == G_FILE_TYPE_DIRECTORY)
+ {
+ GFile *objdir = g_file_get_child (objectdir, name);
+ if (!iter_object_dir (self, objdir, callback, user_data, error))
+ {
+ g_object_unref (objdir);
+ goto out;
+ }
+ g_object_unref (objdir);
+ }
+ g_object_unref (file_info);
+ }
+ if (file_info == NULL && temp_error != NULL)
+ {
+ g_propagate_error (error, temp_error);
+ goto out;
+ }
+ if (!g_file_enumerator_close (enumerator, NULL, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ g_clear_object (&file_info);
+ g_clear_object (&enumerator);
+ g_clear_object (&objectdir);
+ return ret;
+}
+
+gboolean
+ostree_repo_load_variant (OstreeRepo *self,
+ const char *sha256,
+ OstreeSerializedVariantType *out_type,
+ GVariant **out_variant,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ OstreeSerializedVariantType ret_type;
+ GVariant *ret_variant = NULL;
+ char *path = NULL;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ path = get_object_path (self, sha256, OSTREE_OBJECT_TYPE_META);
+ if (!ostree_parse_metadata_file (path, &ret_type, &ret_variant, error))
+ goto out;
+
+ ret = TRUE;
+ *out_type = ret_type;
+ *out_variant = ret_variant;
+ ret_variant = NULL;
+ out:
+ if (ret_variant)
+ g_variant_unref (ret_variant);
+ g_free (path);
+ return ret;
+}
+
+static gboolean
+checkout_tree (OstreeRepo *self,
+ ParsedTreeData *tree,
+ const char *destination,
+ GError **error);
+
+static gboolean
+checkout_one_directory (OstreeRepo *self,
+ const char *destination,
+ const char *dirname,
+ ParsedDirectoryData *dir,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ char *dest_path = NULL;
+ guint32 version, uid, gid, mode;
+ GVariant *xattr_variant = NULL;
+
+ dest_path = g_build_filename (destination, dirname, NULL);
+
+ /* PARSE OSTREE_SERIALIZED_DIRMETA_VARIANT */
+ g_variant_get (dir->meta_data, "(uuuu@a(ayay))",
+ &version, &uid, &gid, &mode,
+ &xattr_variant);
+ version = GUINT32_FROM_BE (version);
+ uid = GUINT32_FROM_BE (uid);
+ gid = GUINT32_FROM_BE (gid);
+ mode = GUINT32_FROM_BE (mode);
+
+ if (mkdir (dest_path, (mode_t)mode) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ g_prefix_error (error, "Failed to create directory '%s': ", dest_path);
+ goto out;
+ }
+
+ if (!checkout_tree (self, dir->tree_data, dest_path, error))
+ goto out;
+
+ if (!ostree_set_xattrs (dest_path, xattr_variant, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ g_free (dest_path);
+ g_variant_unref (xattr_variant);
+ return ret;
+}
+
+static gboolean
+checkout_tree (OstreeRepo *self,
+ ParsedTreeData *tree,
+ const char *destination,
+ GError **error)
+{
+ OstreeRepoPrivate *priv = GET_PRIVATE (self);
+ gboolean ret = FALSE;
+ GHashTableIter hash_iter;
+ gpointer key, value;
+
+ g_hash_table_iter_init (&hash_iter, tree->files);
+ while (g_hash_table_iter_next (&hash_iter, &key, &value))
+ {
+ const char *filename = key;
+ const char *checksum = value;
+ char *object_path;
+ char *dest_path;
+
+ object_path = get_object_path (self, checksum, OSTREE_OBJECT_TYPE_FILE);
+ dest_path = g_build_filename (destination, filename, NULL);
+
+ if (priv->archive)
+ {
+ if (!ostree_unpack_object (object_path, OSTREE_OBJECT_TYPE_FILE, dest_path, NULL, error))
+ goto out;
+ }
+ else
+ {
+ if (link (object_path, dest_path) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ g_free (object_path);
+ g_free (dest_path);
+ goto out;
+ }
+ g_free (object_path);
+ g_free (dest_path);
+ }
+ }
+
+ g_hash_table_iter_init (&hash_iter, tree->directories);
+ while (g_hash_table_iter_next (&hash_iter, &key, &value))
+ {
+ const char *dirname = key;
+ ParsedDirectoryData *dir = value;
+
+ if (!checkout_one_directory (self, destination, dirname, dir, error))
+ goto out;
+ }
+
+ ret = TRUE;
+ out:
+ return ret;
+}
+
+gboolean
+ostree_repo_checkout (OstreeRepo *self,
+ const char *rev,
+ const char *destination,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GVariant *commit = NULL;
+ char *resolved = NULL;
+ char *root_meta_sha = NULL;
+ ParsedDirectoryData *root = NULL;
+
+ if (g_file_test (destination, G_FILE_TEST_EXISTS))
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Destination path '%s' already exists",
+ destination);
+ goto out;
+ }
+
+ if (!resolve_rev (self, rev, FALSE, &resolved, error))
+ goto out;
+
+ if (!load_commit_and_trees (self, resolved, &commit, &root, error))
+ goto out;
+
+ if (!checkout_one_directory (self, destination, NULL, root, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ g_free (resolved);
+ if (commit)
+ g_variant_unref (commit);
+ parsed_directory_data_free (root);
+ g_free (root_meta_sha);
+ return ret;
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+/* ostree-repo.h */
+
+#ifndef _OSTREE_REPO
+#define _OSTREE_REPO
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define OSTREE_TYPE_REPO ostree_repo_get_type()
+#define OSTREE_REPO(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), OSTREE_TYPE_REPO, OstreeRepo))
+#define OSTREE_REPO_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), OSTREE_TYPE_REPO, OstreeRepoClass))
+#define OSTREE_IS_REPO(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), OSTREE_TYPE_REPO))
+#define OSTREE_IS_REPO_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), OSTREE_TYPE_REPO))
+#define OSTREE_REPO_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), OSTREE_TYPE_REPO, OstreeRepoClass))
+
+typedef struct {
+ GObject parent;
+} OstreeRepo;
+
+typedef struct {
+ GObjectClass parent_class;
+} OstreeRepoClass;
+
+GType ostree_repo_get_type (void);
+
+OstreeRepo* ostree_repo_new (const char *path);
+
+gboolean ostree_repo_check (OstreeRepo *self, GError **error);
+
+const char * ostree_repo_get_path (OstreeRepo *self);
+
+gboolean ostree_repo_is_archive (OstreeRepo *self);
+
+GKeyFile * ostree_repo_get_config (OstreeRepo *self);
+
+GKeyFile * ostree_repo_copy_config (OstreeRepo *self);
+
+gboolean ostree_repo_write_config (OstreeRepo *self,
+ GKeyFile *new_config,
+ GError **error);
+
+gboolean ostree_repo_link_file (OstreeRepo *self,
+ const char *path,
+ gboolean ignore_exists,
+ gboolean force,
+ GError **error);
+
+gboolean ostree_repo_store_packfile (OstreeRepo *self,
+ const char *expected_checksum,
+ const char *path,
+ OstreeObjectType objtype,
+ GError **error);
+
+gboolean ostree_repo_store_object_trusted (OstreeRepo *self,
+ const char *path,
+ const char *checksum,
+ OstreeObjectType objtype,
+ gboolean ignore_exists,
+ gboolean force,
+ gboolean *did_exist,
+ GError **error);
+
+gboolean ostree_repo_resolve_rev (OstreeRepo *self,
+ const char *rev,
+ char **out_resolved,
+ GError **error);
+
+gboolean ostree_repo_write_ref (OstreeRepo *self,
+ gboolean is_local,
+ const char *name,
+ const char *rev,
+ GError **error);
+
+gboolean ostree_repo_load_variant (OstreeRepo *self,
+ const char *sha256,
+ OstreeSerializedVariantType *out_type,
+ GVariant **out_variant,
+ GError **error);
+
+gboolean ostree_repo_load_variant_checked (OstreeRepo *self,
+ OstreeSerializedVariantType expected_type,
+ const char *sha256,
+ GVariant **out_variant,
+ GError **error);
+
+gboolean ostree_repo_commit (OstreeRepo *self,
+ const char *branch,
+ const char *parent,
+ const char *subject,
+ const char *body,
+ GVariant *metadata,
+ const char *base,
+ GPtrArray *modified_files,
+ GPtrArray *removed_files,
+ GChecksum **out_commit,
+ GError **error);
+
+gboolean ostree_repo_commit_from_filelist_fd (OstreeRepo *self,
+ const char *branch,
+ const char *parent,
+ const char *subject,
+ const char *body,
+ GVariant *metadata,
+ const char *base,
+ int fd,
+ char separator,
+ GChecksum **out_commit,
+ GError **error);
+
+gboolean ostree_repo_checkout (OstreeRepo *self,
+ const char *ref,
+ const char *destination,
+ GError **error);
+
+typedef void (*OstreeRepoObjectIter) (OstreeRepo *self, const char *path,
+ GFileInfo *fileinfo, gpointer user_data);
+
+gboolean ostree_repo_iter_objects (OstreeRepo *self,
+ OstreeRepoObjectIter callback,
+ gpointer user_data,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* _OSTREE_REPO */
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef __OSTREE_H__
+
+#include <ostree-core.h>
+#include <ostree-repo.h>
+#include <ostree-checkout.h>
+
+#endif
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+#include <gio/gunixinputstream.h>
+
+#include <string.h>
+
+#include "otutil.h"
+
+gboolean
+ot_util_ensure_directory (const char *path, gboolean with_parents, GError **error)
+{
+ GFile *dir;
+ GError *temp_error = NULL;
+ gboolean ret = FALSE;
+
+ dir = g_file_new_for_path (path);
+ if (with_parents)
+ ret = g_file_make_directory_with_parents (dir, NULL, &temp_error);
+ else
+ ret = g_file_make_directory (dir, NULL, &temp_error);
+ if (!ret)
+ {
+ if (!g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_EXISTS))
+ {
+ g_propagate_error (error, temp_error);
+ goto out;
+ }
+ else
+ g_clear_error (&temp_error);
+ }
+
+ ret = TRUE;
+ out:
+ g_clear_object (&dir);
+ return ret;
+}
+
+
+char *
+ot_util_get_file_contents_utf8 (const char *path,
+ GError **error)
+{
+ GFile *file = NULL;
+ char *ret = NULL;
+
+ file = ot_util_new_file_for_path (path);
+ if (!ot_util_gfile_load_contents_utf8 (file, NULL, &ret, NULL, error))
+ goto out;
+
+ out:
+ g_clear_object (&file);
+ return ret;
+}
+
+gboolean
+ot_util_gfile_load_contents_utf8 (GFile *file,
+ GCancellable *cancellable,
+ char **contents_out,
+ char **etag_out,
+ GError **error)
+{
+ char *ret_contents = NULL;
+ char *ret_etag = NULL;
+ gsize len;
+ gboolean ret = FALSE;
+
+ if (!g_file_load_contents (file, cancellable, &ret_contents, &len, &ret_etag, error))
+ goto out;
+ if (!g_utf8_validate (ret_contents, len, NULL))
+ {
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_INVALID_DATA,
+ "Invalid UTF-8");
+ goto out;
+ }
+
+ if (contents_out)
+ *contents_out = ret_contents;
+ else
+ g_free (ret_contents);
+ ret_contents = NULL;
+ if (etag_out)
+ *etag_out = ret_etag;
+ else
+ g_free (ret_etag);
+ ret_etag = NULL;
+ ret = TRUE;
+ out:
+ g_free (ret_contents);
+ g_free (ret_etag);
+ return ret;
+}
+
+GInputStream *
+ot_util_read_file_noatime (GFile *file, GCancellable *cancellable, GError **error)
+{
+ GInputStream *ret = NULL;
+ int fd;
+ int flags = O_RDONLY;
+ char *path = NULL;
+
+ path = g_file_get_path (file);
+#ifdef O_NOATIME
+ flags |= O_NOATIME;
+#endif
+ fd = open (path, flags);
+ if (fd < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+
+ ret = (GInputStream*)g_unix_input_stream_new (fd, TRUE);
+
+ out:
+ g_free (path);
+ return ret;
+}
+
+/* Like g_file_new_for_path, but only do local stuff, not GVFS */
+GFile *
+ot_util_new_file_for_path (const char *path)
+{
+ return g_vfs_get_file_for_path (g_vfs_get_local (), path);
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef __OSTREE_GIO_UTILS_H__
+#define __OSTREE_GIO_UTILS_H__
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+GFile *ot_util_new_file_for_path (const char *path);
+
+gboolean ot_util_ensure_directory (const char *path, gboolean with_parents, GError **error);
+
+char * ot_util_get_file_contents_utf8 (const char *path, GError **error);
+
+gboolean ot_util_gfile_load_contents_utf8 (GFile *file,
+ GCancellable *cancellable,
+ char **contents_out,
+ char **etag_out,
+ GError **error);
+
+GInputStream *ot_util_read_file_noatime (GFile *file, GCancellable *cancellable, GError **error);
+
+G_END_DECLS
+
+#endif
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+
+#include <string.h>
+
+#include "otutil.h"
+
+void
+ot_util_usage_error (GOptionContext *context, const char *message, GError **error)
+{
+ gchar *help = g_option_context_get_help (context, TRUE, NULL);
+ g_printerr ("%s\n", help);
+ g_free (help);
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, message);
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef __OSTREE_GIO_UTILS_H__
+#define __OSTREE_GIO_UTILS_H__
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+void ot_util_usage_error (GOptionContext *context, const char *message, GError **error);
+
+G_END_DECLS
+
+#endif
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-unix-utils.h"
+
+#include <gio/gio.h>
+#include <gio/gunixoutputstream.h>
+
+#include <string.h>
+#include <sys/types.h>
+#include <dirent.h>
+
+gboolean
+ot_util_spawn_pager (GOutputStream **out_stream,
+ GError **error)
+{
+ const char *pager;
+ char *argv[2];
+ int stdin_fd;
+ pid_t pid;
+ gboolean ret = FALSE;
+ GOutputStream *ret_stream = NULL;
+
+ if (!isatty (1))
+ {
+ ret_stream = (GOutputStream*)g_unix_output_stream_new (1, TRUE);
+ }
+ else
+ {
+ pager = g_getenv ("GIT_PAGER");
+ if (pager == NULL)
+ pager = "less";
+
+ argv[0] = (char*)pager;
+ argv[1] = NULL;
+
+ if (!g_spawn_async_with_pipes (NULL, argv, NULL, G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
+ NULL, NULL, &pid, &stdin_fd, NULL, NULL, error))
+ {
+ g_prefix_error (error, "%s", "Failed to spawn pager: ");
+ goto out;
+ }
+
+ ret_stream = (GOutputStream*)g_unix_output_stream_new (stdin_fd, TRUE);
+ }
+
+ *out_stream = ret_stream;
+ ret_stream = NULL;
+ ret = TRUE;
+ out:
+ g_clear_object (&ret_stream);
+ return ret;
+}
+
+static int
+compare_filenames_by_component_length (const char *a,
+ const char *b)
+{
+ char *a_slash, *b_slash;
+
+ a_slash = strchr (a, '/');
+ b_slash = strchr (b, '/');
+ while (a_slash && b_slash)
+ {
+ a = a_slash + 1;
+ b = b_slash + 1;
+ a_slash = strchr (a, '/');
+ b_slash = strchr (b, '/');
+ }
+ if (a_slash)
+ return -1;
+ else if (b_slash)
+ return 1;
+ else
+ return 0;
+}
+
+GPtrArray *
+ot_util_sort_filenames_by_component_length (GPtrArray *files)
+{
+ GPtrArray *array = g_ptr_array_sized_new (files->len);
+ memcpy (array->pdata, files->pdata, sizeof (gpointer) * files->len);
+ g_ptr_array_sort (array, (GCompareFunc) compare_filenames_by_component_length);
+ return array;
+}
+
+int
+ot_util_count_filename_components (const char *path)
+{
+ int i = 0;
+
+ while (path)
+ {
+ i++;
+ path = strchr (path, '/');
+ if (path)
+ path++;
+ }
+ return i;
+}
+
+gboolean
+ot_util_filename_has_dotdot (const char *path)
+{
+ char *p;
+ char last;
+
+ if (strcmp (path, "..") == 0)
+ return TRUE;
+ if (g_str_has_prefix (path, "../"))
+ return TRUE;
+ p = strstr (path, "/..");
+ if (!p)
+ return FALSE;
+ last = *(p + 1);
+ return last == '\0' || last == '/';
+}
+
+GPtrArray *
+ot_util_path_split (const char *path)
+{
+ GPtrArray *ret = NULL;
+ const char *p;
+ const char *slash;
+ int i;
+
+ g_return_val_if_fail (path[0] != '/', NULL);
+
+ ret = g_ptr_array_new ();
+ g_ptr_array_set_free_func (ret, g_free);
+
+ p = path;
+ do {
+ slash = strchr (p, '/');
+ if (!slash)
+ {
+ g_ptr_array_add (ret, g_strdup (p));
+ p = NULL;
+ }
+ else
+ {
+ g_ptr_array_add (ret, g_strndup (p, slash - p));
+ p = slash + 1;
+ }
+ } while (p && *p);
+
+ /* Canonicalize by removing duplicate '.' */
+ for (i = ret->len-1; i >= 0; i--)
+ {
+ if (strcmp (ret->pdata[i], ".") == 0)
+ g_ptr_array_remove_index (ret, i);
+ }
+
+ return ret;
+}
+
+char *
+ot_util_path_join_n (const char *base, GPtrArray *components, int n)
+{
+ int max = MIN(n+1, components->len);
+ GPtrArray *subcomponents;
+ char *path;
+ int i;
+
+ subcomponents = g_ptr_array_new ();
+
+ if (base != NULL)
+ g_ptr_array_add (subcomponents, (char*)base);
+
+ for (i = 0; i < max; i++)
+ {
+ g_ptr_array_add (subcomponents, components->pdata[i]);
+ }
+ g_ptr_array_add (subcomponents, NULL);
+
+ path = g_build_filenamev ((char**)subcomponents->pdata);
+ g_ptr_array_free (subcomponents, TRUE);
+
+ return path;
+}
+
+void
+ot_util_set_error_from_errno (GError **error,
+ gint saved_errno)
+{
+ g_set_error_literal (error,
+ G_IO_ERROR,
+ 0,
+ g_strerror (saved_errno));
+ errno = saved_errno;
+}
+
+int
+ot_util_open_file_read (const char *path, GError **error)
+{
+ char *dirname = NULL;
+ char *basename = NULL;
+ DIR *dir = NULL;
+ int fd = -1;
+
+ dirname = g_path_get_dirname (path);
+ basename = g_path_get_basename (path);
+ dir = opendir (dirname);
+ if (dir == NULL)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+
+ fd = ot_util_open_file_read_at (dirfd (dir), basename, error);
+
+ out:
+ g_free (basename);
+ g_free (dirname);
+ if (dir != NULL)
+ closedir (dir);
+ return fd;
+}
+
+int
+ot_util_open_file_read_at (int dirfd, const char *name, GError **error)
+{
+ int fd;
+ int flags = O_RDONLY;
+
+#ifdef O_CLOEXEC
+ flags |= O_CLOEXEC;
+#endif
+#ifdef O_NOATIME
+ flags |= O_NOATIME;
+#endif
+ fd = openat (dirfd, name, flags);
+ if (fd < 0)
+ ot_util_set_error_from_errno (error, errno);
+ return fd;
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef __OSTREE_UNIX_UTILS_H__
+#define __OSTREE_UNIX_UTILS_H__
+
+#include <gio/gio.h>
+
+/* I just put all this shit here. Sue me. */
+#include <sys/types.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+G_BEGIN_DECLS
+
+gboolean ot_util_spawn_pager (GOutputStream **out_stream, GError **error);
+
+gboolean ot_util_filename_has_dotdot (const char *path);
+
+GPtrArray *ot_util_sort_filenames_by_component_length (GPtrArray *files);
+
+GPtrArray* ot_util_path_split (const char *path);
+
+char *ot_util_path_join_n (const char *base, GPtrArray *components, int n);
+
+int ot_util_count_filename_components (const char *path);
+
+int ot_util_open_file_read (const char *path, GError **error);
+
+int ot_util_open_file_read_at (int dirfd, const char *name, GError **error);
+
+void ot_util_set_error_from_errno (GError **error, gint saved_errno);
+
+G_END_DECLS
+
+#endif
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef __OSTREE_UTIL_H__
+
+#include <ot-unix-utils.h>
+#include <ot-gio-utils.h>
+#include <ot-opt-utils.h>
+
+#endif
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+
+#include <string.h>
+
+#include "ot-builtins.h"
+
+static OstreeBuiltin builtins[] = {
+ { "checkout", ostree_builtin_checkout, 0 },
+ { "init", ostree_builtin_init, 0 },
+ { "commit", ostree_builtin_commit, 0 },
+ { "link-file", ostree_builtin_link_file, 0 },
+ { "log", ostree_builtin_log, 0 },
+ { "pull", ostree_builtin_pull, 0 },
+ { "fsck", ostree_builtin_fsck, 0 },
+ { "remote", ostree_builtin_remote, 0 },
+ { "rev-parse", ostree_builtin_rev_parse, 0 },
+ { "remote", ostree_builtin_remote, 0 },
+ { "run-triggers", ostree_builtin_run_triggers, 0 },
+ { "show", ostree_builtin_show, 0 },
+ { NULL }
+};
+
+static int
+usage (char **argv, gboolean is_error)
+{
+ OstreeBuiltin *builtin = builtins;
+ void (*print_func) (const gchar *format, ...);
+
+ if (is_error)
+ print_func = g_printerr;
+ else
+ print_func = g_print;
+
+ print_func ("usage: %s COMMAND [options]\n",
+ argv[0]);
+ print_func ("Builtin commands:\n");
+
+ while (builtin->name)
+ {
+ print_func (" %s\n", builtin->name);
+ builtin++;
+ }
+ return (is_error ? 1 : 0);
+}
+
+
+int
+main (int argc,
+ char **argv)
+{
+ OstreeBuiltin *builtin;
+ const char *cmd;
+
+ g_type_init ();
+
+ g_set_prgname (argv[0]);
+
+ builtin = builtins;
+
+ if (argc < 2)
+ return usage (argv, 1);
+
+ cmd = argv[1];
+
+ while (builtin->name)
+ {
+ GError *error = NULL;
+ if (strcmp (cmd, builtin->name) == 0)
+ {
+ int i;
+ int tmp_argc;
+ char **tmp_argv;
+
+ tmp_argc = argc - 1;
+ tmp_argv = g_new0 (char *, tmp_argc + 1);
+
+ tmp_argv[0] = (char*)builtin->name;
+ for (i = 0; i < tmp_argc; i++)
+ tmp_argv[i+1] = argv[i+2];
+ if (!builtin->fn (tmp_argc, tmp_argv, NULL, &error))
+ {
+ g_free (tmp_argv);
+ g_printerr ("%s\n", error->message);
+ g_clear_error (&error);
+ return 1;
+ }
+ g_free (tmp_argv);
+ return 0;
+ }
+ builtin++;
+ }
+
+ g_printerr ("Unknown command '%s'\n", cmd);
+ return usage (argv, 1);
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+static char *repo_path;
+
+static GOptionEntry options[] = {
+ { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" },
+ { NULL }
+};
+
+gboolean
+ostree_builtin_checkout (int argc, char **argv, const char *prefix, GError **error)
+{
+ GOptionContext *context;
+ gboolean ret = FALSE;
+ OstreeRepo *repo = NULL;
+ OstreeCheckout *checkout = NULL;
+ const char *commit;
+ const char *destination;
+
+ context = g_option_context_new ("COMMIT DESTINATION - Check out a commit into a filesystem tree");
+ g_option_context_add_main_entries (context, options, NULL);
+
+ if (!g_option_context_parse (context, &argc, &argv, error))
+ goto out;
+
+ if (repo_path == NULL)
+ repo_path = ".";
+
+ repo = ostree_repo_new (repo_path);
+ if (!ostree_repo_check (repo, error))
+ goto out;
+
+ if (argc < 3)
+ {
+ gchar *help = g_option_context_get_help (context, TRUE, NULL);
+ g_printerr ("%s\n", help);
+ g_free (help);
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "COMMIT and DESTINATION must be specified");
+ goto out;
+ }
+
+ commit = argv[1];
+ destination = argv[2];
+
+ if (!ostree_repo_checkout (repo, commit, destination, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ if (context)
+ g_option_context_free (context);
+ g_clear_object (&repo);
+ g_clear_object (&checkout);
+ return ret;
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+static char *repo_path;
+static gboolean separator_null;
+static int from_fd = -1;
+static gboolean from_stdin;
+static char *from_file;
+static char *subject;
+static char *body;
+static char *parent;
+static char *branch;
+static char **additions;
+static char **removals;
+
+static GOptionEntry options[] = {
+ { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" },
+ { "subject", 's', 0, G_OPTION_ARG_STRING, &subject, "One line subject", "subject" },
+ { "body", 'm', 0, G_OPTION_ARG_STRING, &body, "Full description", "body" },
+ { "branch", 'b', 0, G_OPTION_ARG_STRING, &branch, "Branch", "branch" },
+ { "parent", 'p', 0, G_OPTION_ARG_STRING, &parent, "Parent commit", "commit" },
+ { "from-fd", 0, 0, G_OPTION_ARG_INT, &from_fd, "Read new tree files from fd", "file descriptor" },
+ { "from-stdin", 0, 0, G_OPTION_ARG_NONE, &from_stdin, "Read new tree files from stdin", "file descriptor" },
+ { "from-file", 0, 0, G_OPTION_ARG_FILENAME, &from_file, "Read new tree files from another file", "path" },
+ { "separator-null", 0, 0, G_OPTION_ARG_NONE, &separator_null, "", "Use '\\0' as filename separator, as with find -print0" },
+ { "add", 'a', 0, G_OPTION_ARG_FILENAME_ARRAY, &additions, "Relative file path to add", "filename" },
+ { "remove", 'r', 0, G_OPTION_ARG_FILENAME_ARRAY, &removals, "Relative file path to remove", "filename" },
+ { NULL }
+};
+
+gboolean
+ostree_builtin_commit (int argc, char **argv, const char *prefix, GError **error)
+{
+ GOptionContext *context;
+ gboolean ret = FALSE;
+ OstreeRepo *repo = NULL;
+ gboolean using_filename_cmdline;
+ gboolean using_filedescriptors;
+ GPtrArray *additions_array = NULL;
+ GPtrArray *removals_array = NULL;
+ GChecksum *commit_checksum = NULL;
+ char **iter;
+
+ context = g_option_context_new ("- Commit a new revision");
+ g_option_context_add_main_entries (context, options, NULL);
+
+ if (!g_option_context_parse (context, &argc, &argv, error))
+ goto out;
+
+ if (repo_path == NULL)
+ repo_path = ".";
+ if (prefix == NULL)
+ prefix = ".";
+
+ repo = ostree_repo_new (repo_path);
+ if (!ostree_repo_check (repo, error))
+ goto out;
+
+ using_filename_cmdline = (removals || additions);
+ using_filedescriptors = (from_file || from_fd >= 0 || from_stdin);
+
+ if (!(using_filename_cmdline || using_filedescriptors))
+ {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "No additions or removals specified");
+ goto out;
+ }
+ if (using_filename_cmdline && using_filedescriptors)
+ {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "File descriptors may not be combined with --add or --remove");
+ goto out;
+ }
+
+ if (!branch)
+ {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "A branch must be specified with --branch");
+ goto out;
+ }
+
+ if (!subject)
+ {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "A subject must be specified with --subject");
+ goto out;
+ }
+
+ if (using_filename_cmdline)
+ {
+ g_assert (removals || additions);
+ additions_array = g_ptr_array_new ();
+ removals_array = g_ptr_array_new ();
+
+ if (additions)
+ for (iter = additions; *iter; iter++)
+ g_ptr_array_add (additions_array, *iter);
+ if (removals)
+ for (iter = removals; *iter; iter++)
+ g_ptr_array_add (removals_array, *iter);
+
+ if (!ostree_repo_commit (repo, branch, parent, subject, body, NULL,
+ prefix, additions_array,
+ removals_array,
+ &commit_checksum,
+ error))
+ goto out;
+ }
+ else if (using_filedescriptors)
+ {
+ char separator = separator_null ? '\0' : '\n';
+ gboolean temp_fd = -1;
+
+ if (from_stdin)
+ from_fd = 0;
+ else if (from_file)
+ {
+ temp_fd = ot_util_open_file_read (from_file, error);
+ if (temp_fd < 0)
+ {
+ g_prefix_error (error, "Failed to open '%s': ", from_file);
+ goto out;
+ }
+ from_fd = temp_fd;
+ }
+ if (!ostree_repo_commit_from_filelist_fd (repo, branch, parent, subject, body, NULL,
+ prefix, from_fd, separator,
+ &commit_checksum, error))
+ {
+ if (temp_fd >= 0)
+ close (temp_fd);
+ goto out;
+ }
+ if (temp_fd >= 0)
+ close (temp_fd);
+ }
+
+ ret = TRUE;
+ g_print ("%s\n", g_checksum_get_string (commit_checksum));
+ out:
+ if (context)
+ g_option_context_free (context);
+ g_clear_object (&repo);
+ if (removals_array)
+ g_ptr_array_free (removals_array, TRUE);
+ if (additions_array)
+ g_ptr_array_free (additions_array, TRUE);
+ if (commit_checksum)
+ g_checksum_free (commit_checksum);
+ return ret;
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+static char *repo_path;
+static gboolean quiet;
+
+static GOptionEntry options[] = {
+ { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", NULL },
+ { "quiet", 'q', 0, G_OPTION_ARG_NONE, &quiet, "Don't display informational messages", NULL },
+ { NULL }
+};
+
+typedef struct {
+ guint n_objects;
+} HtFsckData;
+
+static gboolean
+checksum_packed_file (HtFsckData *data,
+ const char *path,
+ GChecksum **out_checksum,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GChecksum *ret_checksum = NULL;
+ GFile *file = NULL;
+ char *metadata_buf = NULL;
+ GVariant *metadata = NULL;
+ GVariant *xattrs = NULL;
+ GFileInputStream *in = NULL;
+ guint32 metadata_len;
+ guint32 version, uid, gid, mode;
+ guint64 content_len;
+ gsize bytes_read;
+ char buf[8192];
+
+ file = ot_util_new_file_for_path (path);
+
+ in = g_file_read (file, NULL, error);
+ if (!in)
+ goto out;
+
+ if (!g_input_stream_read_all ((GInputStream*)in, &metadata_len, 4, &bytes_read, NULL, error))
+ goto out;
+
+ metadata_len = GUINT32_FROM_BE (metadata_len);
+
+ metadata_buf = g_malloc (metadata_len);
+
+ if (!g_input_stream_read_all ((GInputStream*)in, metadata_buf, metadata_len, &bytes_read, NULL, error))
+ goto out;
+
+ metadata = g_variant_new_from_data (G_VARIANT_TYPE (OSTREE_PACK_FILE_VARIANT_FORMAT),
+ metadata_buf, metadata_len, FALSE, NULL, NULL);
+
+ g_variant_get (metadata, "(uuuu@a(ayay)t)",
+ &version, &uid, &gid, &mode,
+ &xattrs, &content_len);
+ uid = GUINT32_FROM_BE (uid);
+ gid = GUINT32_FROM_BE (gid);
+ mode = GUINT32_FROM_BE (mode);
+ content_len = GUINT64_FROM_BE (content_len);
+
+ ret_checksum = g_checksum_new (G_CHECKSUM_SHA256);
+
+ do
+ {
+ if (!g_input_stream_read_all ((GInputStream*)in, buf, sizeof(buf), &bytes_read, NULL, error))
+ goto out;
+ g_checksum_update (ret_checksum, (guint8*)buf, bytes_read);
+ }
+ while (bytes_read > 0);
+
+ ostree_checksum_update_stat (ret_checksum, uid, gid, mode);
+ g_checksum_update (ret_checksum, (guint8*)g_variant_get_data (xattrs), g_variant_get_size (xattrs));
+
+ ret = TRUE;
+ *out_checksum = ret_checksum;
+ ret_checksum = NULL;
+ out:
+ if (ret_checksum)
+ g_checksum_free (ret_checksum);
+ g_free (metadata_buf);
+ g_clear_object (&file);
+ g_clear_object (&in);
+ if (metadata)
+ g_variant_unref (metadata);
+ if (xattrs)
+ g_variant_unref (xattrs);
+ return ret;
+}
+
+
+static void
+object_iter_callback (OstreeRepo *repo,
+ const char *path,
+ GFileInfo *file_info,
+ gpointer user_data)
+{
+ HtFsckData *data = user_data;
+ struct stat stbuf;
+ GChecksum *checksum = NULL;
+ GError *error = NULL;
+ char *dirname = NULL;
+ char *checksum_prefix = NULL;
+ char *checksum_string = NULL;
+ char *filename_checksum = NULL;
+ gboolean packed = FALSE;
+ OstreeObjectType objtype;
+ char *dot;
+
+ /* nlinks = g_file_info_get_attribute_uint32 (file_info, "unix::nlink");
+ if (nlinks < 2 && !quiet)
+ g_printerr ("note: floating object: %s\n", path); */
+
+ if (g_str_has_suffix (path, ".meta"))
+ objtype = OSTREE_OBJECT_TYPE_META;
+ else if (g_str_has_suffix (path, ".file"))
+ objtype = OSTREE_OBJECT_TYPE_FILE;
+ else if (g_str_has_suffix (path, ".packfile"))
+ {
+ objtype = OSTREE_OBJECT_TYPE_FILE;
+ packed = TRUE;
+ }
+ else
+ g_assert_not_reached ();
+
+ if (packed && objtype == OSTREE_OBJECT_TYPE_FILE)
+ {
+ if (!checksum_packed_file (data, path, &checksum, &error))
+ goto out;
+ }
+ else
+ {
+ if (!ostree_stat_and_checksum_file (-1, path, objtype, &checksum, &stbuf, &error))
+ goto out;
+ }
+
+ filename_checksum = g_strdup (g_file_info_get_name (file_info));
+ dot = strrchr (filename_checksum, '.');
+ g_assert (dot != NULL);
+ *dot = '\0';
+
+ dirname = g_path_get_dirname (path);
+ checksum_prefix = g_path_get_basename (dirname);
+ checksum_string = g_strconcat (checksum_prefix, filename_checksum, NULL);
+
+ if (strcmp (checksum_string, g_checksum_get_string (checksum)) != 0)
+ {
+ g_printerr ("ERROR: corrupted object '%s' expected checksum: %s\n",
+ path, g_checksum_get_string (checksum));
+ }
+
+ data->n_objects++;
+
+ out:
+ if (checksum != NULL)
+ g_checksum_free (checksum);
+ g_free (dirname);
+ g_free (checksum_prefix);
+ g_free (checksum_string);
+ g_free (filename_checksum);
+ if (error != NULL)
+ {
+ g_printerr ("%s\n", error->message);
+ g_clear_error (&error);
+ }
+}
+
+gboolean
+ostree_builtin_fsck (int argc, char **argv, const char *prefix, GError **error)
+{
+ GOptionContext *context;
+ HtFsckData data;
+ gboolean ret = FALSE;
+ OstreeRepo *repo = NULL;
+
+ context = g_option_context_new ("- Check the repository for consistency");
+ g_option_context_add_main_entries (context, options, NULL);
+
+ if (!g_option_context_parse (context, &argc, &argv, error))
+ goto out;
+
+ if (repo_path == NULL)
+ repo_path = ".";
+
+ data.n_objects = 0;
+
+ repo = ostree_repo_new (repo_path);
+ if (!ostree_repo_check (repo, error))
+ goto out;
+
+ if (!ostree_repo_iter_objects (repo, object_iter_callback, &data, error))
+ goto out;
+
+ if (!quiet)
+ g_printerr ("Total Objects: %u\n", data.n_objects);
+
+ ret = TRUE;
+ out:
+ if (context)
+ g_option_context_free (context);
+ g_clear_object (&repo);
+ return ret;
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+static char *repo_path;
+static gboolean archive;
+
+static GOptionEntry options[] = {
+ { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", NULL },
+ { "archive", 0, 0, G_OPTION_ARG_NONE, &archive, "Initialize repository as archive", NULL },
+ { NULL }
+};
+
+#define DEFAULT_CONFIG_CONTENTS ("[core]\n" \
+ "repo_version=0\n")
+
+
+gboolean
+ostree_builtin_init (int argc, char **argv, const char *prefix, GError **error)
+{
+ GOptionContext *context = NULL;
+ gboolean ret = FALSE;
+ GFile *repodir = NULL;
+ GFile *child = NULL;
+ GFile *grandchild = NULL;
+ GString *config_data = NULL;
+
+ context = g_option_context_new ("- Initialize a new empty repository");
+ g_option_context_add_main_entries (context, options, NULL);
+
+ if (!g_option_context_parse (context, &argc, &argv, error))
+ goto out;
+
+ if (repo_path == NULL)
+ repo_path = ".";
+
+ repodir = ot_util_new_file_for_path (repo_path);
+
+ child = g_file_get_child (repodir, "config");
+
+ config_data = g_string_new (DEFAULT_CONFIG_CONTENTS);
+ g_string_append_printf (config_data, "archive=%s\n", archive ? "true" : "false");
+ if (!g_file_replace_contents (child,
+ config_data->str,
+ config_data->len,
+ NULL, FALSE, 0, NULL,
+ NULL, error))
+ goto out;
+ g_clear_object (&child);
+
+ child = g_file_get_child (repodir, "objects");
+ if (!g_file_make_directory (child, NULL, error))
+ goto out;
+ g_clear_object (&child);
+
+ child = g_file_get_child (repodir, "refs");
+ if (!g_file_make_directory (child, NULL, error))
+ goto out;
+
+ grandchild = g_file_get_child (child, "heads");
+ if (!g_file_make_directory (grandchild, NULL, error))
+ goto out;
+ g_clear_object (&grandchild);
+
+ grandchild = g_file_get_child (child, "remotes");
+ if (!g_file_make_directory (grandchild, NULL, error))
+ goto out;
+ g_clear_object (&grandchild);
+
+ g_clear_object (&child);
+
+ child = g_file_get_child (repodir, "tags");
+ if (!g_file_make_directory (child, NULL, error))
+ goto out;
+ g_clear_object (&child);
+
+ ret = TRUE;
+ out:
+ if (context)
+ g_option_context_free (context);
+ if (config_data)
+ g_string_free (config_data, TRUE);
+ g_clear_object (&repodir);
+ g_clear_object (&child);
+ g_clear_object (&grandchild);
+ return ret;
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+static char *repo_path;
+static gboolean ignore_exists;
+static gboolean force;
+
+static GOptionEntry options[] = {
+ { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" },
+ { "ignore-exists", 'n', 0, G_OPTION_ARG_NONE, &ignore_exists, "Don't error if file exists", NULL },
+ { "force", 'f', 0, G_OPTION_ARG_NONE, &force, "If object exists, relink file", NULL },
+ { NULL }
+};
+
+gboolean
+ostree_builtin_link_file (int argc, char **argv, const char *prefix, GError **error)
+{
+ GOptionContext *context;
+ gboolean ret = FALSE;
+ OstreeRepo *repo = NULL;
+ int i;
+
+ context = g_option_context_new ("- Create a new hard link in the repository");
+ g_option_context_add_main_entries (context, options, NULL);
+
+ if (!g_option_context_parse (context, &argc, &argv, error))
+ goto out;
+
+ if (repo_path == NULL)
+ repo_path = ".";
+
+ repo = ostree_repo_new (repo_path);
+ if (!ostree_repo_check (repo, error))
+ goto out;
+
+ for (i = 0; i < argc-1; i++)
+ {
+ if (!ostree_repo_link_file (repo, argv[i+1], ignore_exists, force, error))
+ goto out;
+ }
+
+ ret = TRUE;
+ out:
+ if (context)
+ g_option_context_free (context);
+ g_clear_object (&repo);
+ return ret;
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+static char *repo_path;
+
+static GOptionEntry options[] = {
+ { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" },
+ { NULL }
+};
+
+gboolean
+ostree_builtin_log (int argc, char **argv, const char *prefix, GError **error)
+{
+ GOptionContext *context;
+ gboolean ret = FALSE;
+ OstreeRepo *repo = NULL;
+ GOutputStream *pager = NULL;
+ const char *rev;
+ GVariant *commit = NULL;
+ char *resolved_rev = NULL;
+
+ context = g_option_context_new ("- Show revision log");
+ g_option_context_add_main_entries (context, options, NULL);
+
+ if (!g_option_context_parse (context, &argc, &argv, error))
+ goto out;
+
+ if (repo_path == NULL)
+ repo_path = ".";
+ if (prefix == NULL)
+ prefix = ".";
+
+ if (argc < 2)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "A revision must be specified");
+ goto out;
+ }
+
+ rev = argv[1];
+
+ repo = ostree_repo_new (repo_path);
+ if (!ostree_repo_check (repo, error))
+ goto out;
+
+ if (!ot_util_spawn_pager (&pager, error))
+ goto out;
+
+ if (!ostree_repo_resolve_rev (repo, rev, &resolved_rev, error))
+ goto out;
+
+ while (TRUE)
+ {
+ OstreeSerializedVariantType type;
+ char *formatted = NULL;
+ guint32 version;
+ const char *parent;
+ const char *subject;
+ const char *body;
+ guint64 timestamp;
+ const char *contents;
+ const char *root_metadata;
+ GDateTime *time_obj = NULL;
+ char *formatted_date = NULL;
+ const char *body_newline;
+ gsize bytes_written;
+ GVariant *commit_metadata = NULL;
+ char *formatted_metadata = NULL;
+
+ if (commit)
+ g_variant_unref (commit);
+ if (!ostree_repo_load_variant (repo, resolved_rev, &type, &commit, error))
+ goto out;
+
+ /* Ignore commit metadata for now */
+ g_variant_get (commit, "(u@a{sv}&s&s&st&s&s)",
+ &version, &commit_metadata, &parent, &subject, &body,
+ ×tamp, &contents, &root_metadata);
+ version = GUINT32_FROM_BE (version);
+ timestamp = GUINT64_FROM_BE (timestamp);
+ time_obj = g_date_time_new_from_unix_utc (timestamp);
+ formatted_date = g_date_time_format (time_obj, "%a %b %d %H:%M:%S %Y %z");
+ g_date_time_unref (time_obj);
+ time_obj = NULL;
+
+ formatted_metadata = g_variant_print (commit_metadata, TRUE);
+ g_variant_unref (commit_metadata);
+ formatted = g_strdup_printf ("commit %s\nSubject: %s\nDate: %s\nMetadata: %s\n\n",
+ resolved_rev, subject, formatted_date, formatted_metadata);
+ g_free (formatted_metadata);
+ g_free (formatted_date);
+ formatted_date = NULL;
+
+ if (!g_output_stream_write_all (pager, formatted, strlen (formatted), &bytes_written, NULL, error))
+ {
+ g_free (formatted);
+ goto out;
+ }
+ g_free (formatted);
+
+ body_newline = strchr (body, '\n');
+ do {
+ gsize len;
+ if (!g_output_stream_write_all (pager, " ", 4, &bytes_written, NULL, error))
+ goto out;
+ len = body_newline ? body_newline - body : strlen (body);
+ if (!g_output_stream_write_all (pager, body, len, &bytes_written, NULL, error))
+ goto out;
+ if (!g_output_stream_write_all (pager, "\n\n", 2, &bytes_written, NULL, error))
+ goto out;
+ body_newline = strchr (body, '\n');
+ if (!body_newline)
+ break;
+ else
+ body_newline += 1;
+ } while (*body_newline);
+
+ if (strcmp (parent, "") == 0)
+ break;
+ g_free (resolved_rev);
+ resolved_rev = g_strdup (parent);
+ }
+
+ if (!g_output_stream_close (pager, NULL, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ g_free (resolved_rev);
+ if (context)
+ g_option_context_free (context);
+ if (commit)
+ g_variant_unref (commit);
+ g_clear_object (&repo);
+ return ret;
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+#include <libsoup/soup-gnome.h>
+
+static char *repo_path;
+
+static GOptionEntry options[] = {
+ { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" },
+ { NULL }
+};
+
+static gboolean
+fetch_uri (OstreeRepo *repo,
+ SoupSession *soup,
+ SoupURI *uri,
+ char **temp_filename,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ SoupMessage *msg = NULL;
+ guint response;
+ char *template = NULL;
+ int fd;
+ SoupBuffer *buf = NULL;
+ GFile *tempf = NULL;
+
+ msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
+
+ response = soup_session_send_message (soup, msg);
+ if (response != 200)
+ {
+ char *uri_string = soup_uri_to_string (uri, FALSE);
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Failed to retrieve '%s': %d %s",
+ uri_string, response, msg->reason_phrase);
+ g_free (uri_string);
+ goto out;
+ }
+
+ template = g_strdup_printf ("%s/tmp-fetchXXXXXX", ostree_repo_get_path (repo));
+
+ fd = g_mkstemp (template);
+ if (fd < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ close (fd);
+ tempf = ot_util_new_file_for_path (template);
+
+ buf = soup_message_body_flatten (msg->response_body);
+
+ if (!g_file_replace_contents (tempf, buf->data, buf->length, NULL, FALSE, 0, NULL, NULL, error))
+ goto out;
+
+ *temp_filename = template;
+ template = NULL;
+
+ ret = TRUE;
+ out:
+ g_free (template);
+ g_clear_object (&msg);
+ g_clear_object (&tempf);
+ return ret;
+}
+
+static gboolean
+store_object (OstreeRepo *repo,
+ SoupSession *soup,
+ SoupURI *baseuri,
+ const char *object,
+ OstreeObjectType objtype,
+ gboolean *did_exist,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ char *filename = NULL;
+ char *objpath = NULL;
+ char *relpath = NULL;
+ SoupURI *obj_uri = NULL;
+
+ objpath = ostree_get_relative_object_path (object, objtype, TRUE);
+ obj_uri = soup_uri_copy (baseuri);
+ relpath = g_build_filename (soup_uri_get_path (obj_uri), objpath, NULL);
+ soup_uri_set_path (obj_uri, relpath);
+
+ if (!fetch_uri (repo, soup, obj_uri, &filename, error))
+ goto out;
+
+ if (!ostree_repo_store_packfile (repo, object, filename, objtype, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ if (obj_uri)
+ soup_uri_free (obj_uri);
+ if (filename)
+ (void) unlink (filename);
+ g_free (filename);
+ g_free (objpath);
+ g_free (relpath);
+ return ret;
+}
+
+static gboolean
+store_tree_recurse (OstreeRepo *repo,
+ SoupSession *soup,
+ SoupURI *base_uri,
+ const char *rev,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GVariant *tree = NULL;
+ GVariant *files_variant = NULL;
+ GVariant *dirs_variant = NULL;
+ OstreeSerializedVariantType metatype;
+ gboolean did_exist;
+ int i, n;
+
+ if (!store_object (repo, soup, base_uri, rev, OSTREE_OBJECT_TYPE_META, &did_exist, error))
+ goto out;
+
+ if (!did_exist)
+ {
+ if (!ostree_repo_load_variant (repo, rev, &metatype, &tree, error))
+ goto out;
+
+ if (metatype != OSTREE_SERIALIZED_TREE_VARIANT)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Tree metadata '%s' has wrong type %d, expected %d",
+ rev, metatype, OSTREE_SERIALIZED_TREE_VARIANT);
+ goto out;
+ }
+
+ /* PARSE OSTREE_SERIALIZED_TREE_VARIANT */
+ g_variant_get_child (tree, 2, "@a(ss)", &files_variant);
+ g_variant_get_child (tree, 3, "@a(sss)", &dirs_variant);
+
+ n = g_variant_n_children (files_variant);
+ for (i = 0; i < n; i++)
+ {
+ const char *filename;
+ const char *checksum;
+
+ g_variant_get_child (files_variant, i, "(ss)", &filename, &checksum);
+
+ if (!store_object (repo, soup, base_uri, checksum, OSTREE_OBJECT_TYPE_FILE, &did_exist, error))
+ goto out;
+ }
+
+ for (i = 0; i < n; i++)
+ {
+ const char *dirname;
+ const char *tree_checksum;
+ const char *meta_checksum;
+
+ g_variant_get_child (dirs_variant, i, "(sss)",
+ &dirname, &tree_checksum, &meta_checksum);
+
+ if (!store_tree_recurse (repo, soup, base_uri, tree_checksum, error))
+ goto out;
+
+ if (!store_object (repo, soup, base_uri, meta_checksum, OSTREE_OBJECT_TYPE_META, &did_exist, error))
+ goto out;
+ }
+ }
+
+ ret = TRUE;
+ out:
+ if (tree)
+ g_variant_unref (tree);
+ if (files_variant)
+ g_variant_unref (files_variant);
+ if (dirs_variant)
+ g_variant_unref (dirs_variant);
+ return ret;
+}
+
+static gboolean
+store_commit_recurse (OstreeRepo *repo,
+ SoupSession *soup,
+ SoupURI *base_uri,
+ const char *rev,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ GVariant *commit = NULL;
+ OstreeSerializedVariantType metatype;
+ const char *tree_contents_checksum;
+ const char *tree_meta_checksum;
+ gboolean did_exist;
+
+ if (!store_object (repo, soup, base_uri, rev, OSTREE_OBJECT_TYPE_META, &did_exist, error))
+ goto out;
+
+ if (!did_exist)
+ {
+ if (!ostree_repo_load_variant (repo, rev, &metatype, &commit, error))
+ goto out;
+
+ if (metatype != OSTREE_SERIALIZED_COMMIT_VARIANT)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Commit '%s' has wrong type %d, expected %d",
+ rev, metatype, OSTREE_SERIALIZED_COMMIT_VARIANT);
+ goto out;
+ }
+
+ /* PARSE OSTREE_SERIALIZED_COMMIT_VARIANT */
+ g_variant_get_child (commit, 6, "&s", &tree_contents_checksum);
+ g_variant_get_child (commit, 7, "&s", &tree_meta_checksum);
+
+ if (!store_object (repo, soup, base_uri, tree_meta_checksum, OSTREE_OBJECT_TYPE_META, &did_exist, error))
+ goto out;
+
+ if (!store_tree_recurse (repo, soup, base_uri, tree_contents_checksum, error))
+ goto out;
+ }
+
+ ret = TRUE;
+ out:
+ if (commit)
+ g_variant_unref (commit);
+ return ret;
+}
+
+gboolean
+ostree_builtin_pull (int argc, char **argv, const char *prefix, GError **error)
+{
+ GOptionContext *context;
+ gboolean ret = FALSE;
+ OstreeRepo *repo = NULL;
+ const char *remote;
+ const char *branch;
+ char *remote_branch_ref_path = NULL;
+ char *key = NULL;
+ char *baseurl = NULL;
+ char *refpath = NULL;
+ char *temppath = NULL;
+ GKeyFile *config = NULL;
+ SoupURI *base_uri = NULL;
+ SoupURI *target_uri = NULL;
+ SoupSession *soup = NULL;
+ char *rev = NULL;
+
+ context = g_option_context_new ("REMOTE BRANCH - Download data from remote repository");
+ g_option_context_add_main_entries (context, options, NULL);
+
+ if (!g_option_context_parse (context, &argc, &argv, error))
+ goto out;
+
+ if (repo_path == NULL)
+ repo_path = ".";
+
+ repo = ostree_repo_new (repo_path);
+ if (!ostree_repo_check (repo, error))
+ goto out;
+
+ if (argc < 3)
+ {
+ ot_util_usage_error (context, "REMOTE and BRANCH must be specified", error);
+ goto out;
+ }
+
+ remote = argv[1];
+ branch = argv[2];
+
+ config = ostree_repo_get_config (repo);
+
+ key = g_strdup_printf ("remote \"%s\"", remote);
+ baseurl = g_key_file_get_string (config, key, "url", error);
+ if (!baseurl)
+ goto out;
+ base_uri = soup_uri_new (baseurl);
+ if (!base_uri)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Failed to parse url '%s'", baseurl);
+ goto out;
+ }
+ target_uri = soup_uri_copy (base_uri);
+ g_free (refpath);
+ refpath = g_build_filename (soup_uri_get_path (target_uri), "refs", "heads", branch, NULL);
+ soup_uri_set_path (target_uri, refpath);
+
+ soup = soup_session_sync_new_with_options (SOUP_SESSION_USER_AGENT, "ostree ",
+ SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_GNOME_FEATURES_2_26,
+ SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_CONTENT_DECODER,
+ SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_COOKIE_JAR,
+ NULL);
+ if (!fetch_uri (repo, soup, target_uri, &temppath, error))
+ goto out;
+
+ rev = ot_util_get_file_contents_utf8 (temppath, error);
+ if (!rev)
+ goto out;
+ g_strchomp (rev);
+
+ if (!ostree_validate_checksum_string (rev, error))
+ goto out;
+
+ if (!store_commit_recurse (repo, soup, base_uri, rev, error))
+ goto out;
+
+ if (!ostree_repo_write_ref (repo, FALSE, branch, rev, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ if (context)
+ g_option_context_free (context);
+ if (temppath)
+ (void) unlink (temppath);
+ g_free (temppath);
+ g_free (key);
+ g_free (rev);
+ g_free (baseurl);
+ g_free (refpath);
+ g_free (remote_branch_ref_path);
+ g_clear_object (&soup);
+ if (base_uri)
+ soup_uri_free (base_uri);
+ if (target_uri)
+ soup_uri_free (target_uri);
+ g_clear_object (&repo);
+ g_clear_object (&soup);
+ return ret;
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+static char *repo_path;
+
+static GOptionEntry options[] = {
+ { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" },
+ { NULL }
+};
+
+static void
+usage_error (GOptionContext *context, const char *message, GError **error)
+{
+ gchar *help = g_option_context_get_help (context, TRUE, NULL);
+ g_printerr ("%s\n", help);
+ g_free (help);
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ message);
+}
+
+gboolean
+ostree_builtin_remote (int argc, char **argv, const char *prefix, GError **error)
+{
+ GOptionContext *context;
+ gboolean ret = FALSE;
+ OstreeRepo *repo = NULL;
+ OstreeCheckout *checkout = NULL;
+ const char *op;
+ GKeyFile *config = NULL;
+
+ context = g_option_context_new ("OPERATION [args] - Control remote repository configuration");
+ g_option_context_add_main_entries (context, options, NULL);
+
+ if (!g_option_context_parse (context, &argc, &argv, error))
+ goto out;
+
+ if (repo_path == NULL)
+ repo_path = ".";
+
+ repo = ostree_repo_new (repo_path);
+ if (!ostree_repo_check (repo, error))
+ goto out;
+
+ if (argc < 2)
+ {
+ usage_error (context, "OPERATION must be specified", error);
+ goto out;
+ }
+
+ op = argv[1];
+
+ config = ostree_repo_copy_config (repo);
+
+ if (!strcmp (op, "add"))
+ {
+ char *key;
+ if (argc < 4)
+ {
+ usage_error (context, "NAME and URL must be specified", error);
+ goto out;
+ }
+ key = g_strdup_printf ("remote \"%s\"", argv[2]);
+ g_key_file_set_string (config, key, "url", argv[3]);
+ g_free (key);
+ }
+ else
+ {
+ usage_error (context, "Unknown operation", error);
+ goto out;
+ }
+
+ if (!ostree_repo_write_config (repo, config, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ if (context)
+ g_option_context_free (context);
+ if (config)
+ g_key_file_unref (config);
+ g_clear_object (&repo);
+ g_clear_object (&checkout);
+ return ret;
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+static char *repo_path;
+
+static GOptionEntry options[] = {
+ { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" },
+ { NULL }
+};
+
+gboolean
+ostree_builtin_rev_parse (int argc, char **argv, const char *prefix, GError **error)
+{
+ GOptionContext *context;
+ gboolean ret = FALSE;
+ OstreeRepo *repo = NULL;
+ const char *rev = "master";
+ char *resolved_rev = NULL;
+ GVariant *variant = NULL;
+ char *formatted_variant = NULL;
+
+ context = g_option_context_new ("REV - Output the target of a rev");
+ g_option_context_add_main_entries (context, options, NULL);
+
+ if (!g_option_context_parse (context, &argc, &argv, error))
+ goto out;
+
+ if (repo_path == NULL)
+ repo_path = ".";
+
+ repo = ostree_repo_new (repo_path);
+ if (!ostree_repo_check (repo, error))
+ goto out;
+
+ if (argc < 2)
+ {
+ ot_util_usage_error (context, "REV must be specified", error);
+ goto out;
+ }
+ rev = argv[1];
+
+ if (!ostree_repo_resolve_rev (repo, rev, &resolved_rev, error))
+ goto out;
+
+ g_print ("%s\n", resolved_rev);
+
+ ret = TRUE;
+ out:
+ g_free (resolved_rev);
+ if (context)
+ g_option_context_free (context);
+ g_clear_object (&repo);
+ if (variant)
+ g_variant_unref (variant);
+ g_free (formatted_variant);
+ return ret;
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+static char *repo_path;
+static gboolean quiet;
+
+static GOptionEntry options[] = {
+ { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" },
+ { "quiet", 'q', 0, G_OPTION_ARG_NONE, &quiet, "Don't display informational messages", NULL },
+ { NULL }
+};
+
+gboolean
+ostree_builtin_run_triggers (int argc, char **argv, const char *prefix, GError **error)
+{
+ GOptionContext *context;
+ gboolean ret = FALSE;
+ OstreeRepo *repo = NULL;
+ OstreeCheckout *checkout = NULL;
+ const char *dir;
+
+ context = g_option_context_new ("DIR - Run trigger scripts for directory");
+ g_option_context_add_main_entries (context, options, NULL);
+
+ if (!g_option_context_parse (context, &argc, &argv, error))
+ goto out;
+
+ if (repo_path == NULL)
+ repo_path = ".";
+
+ repo = ostree_repo_new (repo_path);
+ if (!ostree_repo_check (repo, error))
+ goto out;
+
+ if (argc < 1)
+ {
+ gchar *help = g_option_context_get_help (context, TRUE, NULL);
+ g_printerr ("%s\n", help);
+ g_free (help);
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "DIR must be specified");
+ goto out;
+ }
+
+ dir = argv[1];
+
+ checkout = ostree_checkout_new (repo, dir);
+ if (!ostree_checkout_run_triggers (checkout, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ if (context)
+ g_option_context_free (context);
+ g_clear_object (&repo);
+ g_clear_object (&checkout);
+ return ret;
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+static char *repo_path;
+
+static GOptionEntry options[] = {
+ { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" },
+ { NULL }
+};
+
+gboolean
+ostree_builtin_show (int argc, char **argv, const char *prefix, GError **error)
+{
+ GOptionContext *context;
+ gboolean ret = FALSE;
+ OstreeRepo *repo = NULL;
+ const char *rev = "master";
+ char *resolved_rev = NULL;
+ OstreeSerializedVariantType type;
+ GVariant *variant = NULL;
+ char *formatted_variant = NULL;
+
+ context = g_option_context_new ("- Output a metadata object");
+ g_option_context_add_main_entries (context, options, NULL);
+
+ if (!g_option_context_parse (context, &argc, &argv, error))
+ goto out;
+
+ if (repo_path == NULL)
+ repo_path = ".";
+
+ repo = ostree_repo_new (repo_path);
+ if (!ostree_repo_check (repo, error))
+ goto out;
+
+ if (argc > 1)
+ rev = argv[1];
+
+ if (!ostree_repo_resolve_rev (repo, rev, &resolved_rev, error))
+ goto out;
+
+ if (!ostree_repo_load_variant (repo, resolved_rev, &type, &variant, error))
+ goto out;
+
+ g_print ("Object: %s\nType: %d\n", resolved_rev, type);
+ formatted_variant = g_variant_print (variant, TRUE);
+ g_print ("%s\n", formatted_variant);
+
+ ret = TRUE;
+ out:
+ g_free (resolved_rev);
+ if (context)
+ g_option_context_free (context);
+ g_clear_object (&repo);
+ if (variant)
+ g_variant_unref (variant);
+ g_free (formatted_variant);
+ return ret;
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef __OSTREE_BUILTINS__
+#define __OSTREE_BUILTINS__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+ OSTREE_BUILTIN_FLAG_NONE = 0,
+} OstreeBuiltinFlags;
+
+typedef struct {
+ const char *name;
+ gboolean (*fn) (int argc, char **argv, const char *prefix, GError **error);
+ int flags; /* OstreeBuiltinFlags */
+} OstreeBuiltin;
+
+gboolean ostree_builtin_checkout (int argc, char **argv, const char *prefix, GError **error);
+gboolean ostree_builtin_commit (int argc, char **argv, const char *prefix, GError **error);
+gboolean ostree_builtin_init (int argc, char **argv, const char *prefix, GError **error);
+gboolean ostree_builtin_log (int argc, char **argv, const char *prefix, GError **error);
+gboolean ostree_builtin_link_file (int argc, char **argv, const char *prefix, GError **error);
+gboolean ostree_builtin_pull (int argc, char **argv, const char *prefix, GError **error);
+gboolean ostree_builtin_run_triggers (int argc, char **argv, const char *prefix, GError **error);
+gboolean ostree_builtin_fsck (int argc, char **argv, const char *prefix, GError **error);
+gboolean ostree_builtin_show (int argc, char **argv, const char *prefix, GError **error);
+gboolean ostree_builtin_rev_parse (int argc, char **argv, const char *prefix, GError **error);
+gboolean ostree_builtin_remote (int argc, char **argv, const char *prefix, GError **error);
+
+G_END_DECLS
+
+#endif
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ostree.h"
-#include "otutil.h"
-
-enum {
- PROP_0,
-
- PROP_REPO,
- PROP_PATH
-};
-
-G_DEFINE_TYPE (OstreeCheckout, ostree_checkout, G_TYPE_OBJECT)
-
-#define GET_PRIVATE(o) \
- (G_TYPE_INSTANCE_GET_PRIVATE ((o), OSTREE_TYPE_CHECKOUT, OstreeCheckoutPrivate))
-
-typedef struct _OstreeCheckoutPrivate OstreeCheckoutPrivate;
-
-struct _OstreeCheckoutPrivate {
- OstreeRepo *repo;
- char *path;
-};
-
-static void
-ostree_checkout_finalize (GObject *object)
-{
- OstreeCheckout *self = OSTREE_CHECKOUT (object);
- OstreeCheckoutPrivate *priv = GET_PRIVATE (self);
-
- g_free (priv->path);
- g_clear_object (&priv->repo);
-
- G_OBJECT_CLASS (ostree_checkout_parent_class)->finalize (object);
-}
-
-static void
-ostree_checkout_set_property(GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- OstreeCheckout *self = OSTREE_CHECKOUT (object);
- OstreeCheckoutPrivate *priv = GET_PRIVATE (self);
-
- switch (prop_id)
- {
- case PROP_PATH:
- priv->path = g_value_dup_string (value);
- break;
- case PROP_REPO:
- priv->repo = g_value_dup_object (value);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static void
-ostree_checkout_get_property(GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec)
-{
- OstreeCheckout *self = OSTREE_CHECKOUT (object);
- OstreeCheckoutPrivate *priv = GET_PRIVATE (self);
-
- switch (prop_id)
- {
- case PROP_PATH:
- g_value_set_string (value, priv->path);
- break;
- case PROP_REPO:
- g_value_set_object (value, priv->repo);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static GObject *
-ostree_checkout_constructor (GType gtype,
- guint n_properties,
- GObjectConstructParam *properties)
-{
- GObject *object;
- GObjectClass *parent_class;
- OstreeCheckoutPrivate *priv;
-
- parent_class = G_OBJECT_CLASS (ostree_checkout_parent_class);
- object = parent_class->constructor (gtype, n_properties, properties);
-
- priv = GET_PRIVATE (object);
-
- g_assert (priv->path != NULL);
-
- return object;
-}
-
-static void
-ostree_checkout_class_init (OstreeCheckoutClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
- g_type_class_add_private (klass, sizeof (OstreeCheckoutPrivate));
-
- object_class->constructor = ostree_checkout_constructor;
- object_class->get_property = ostree_checkout_get_property;
- object_class->set_property = ostree_checkout_set_property;
- object_class->finalize = ostree_checkout_finalize;
-
- g_object_class_install_property (object_class,
- PROP_PATH,
- g_param_spec_string ("path", "", "",
- NULL,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-
- g_object_class_install_property (object_class,
- PROP_REPO,
- g_param_spec_object ("repo", "", "",
- OSTREE_TYPE_REPO,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-}
-
-static void
-ostree_checkout_init (OstreeCheckout *self)
-{
-}
-
-OstreeCheckout*
-ostree_checkout_new (OstreeRepo *repo,
- const char *path)
-{
- return g_object_new (OSTREE_TYPE_CHECKOUT, "repo", repo, "path", path, NULL);
-}
-
-static gboolean
-executable_exists_in_checkout (const char *path,
- const char *executable)
-{
- int i;
- const char *subdirs[] = {"bin", "sbin", "usr/bin", "usr/sbin"};
-
- for (i = 0; i < G_N_ELEMENTS (subdirs); i++)
- {
- char *possible_path = g_build_filename (path, subdirs[i], executable, NULL);
- gboolean exists;
-
- exists = g_file_test (possible_path, G_FILE_TEST_EXISTS);
- g_free (possible_path);
-
- if (exists)
- return TRUE;
- }
-
- return FALSE;
-}
-
-static gboolean
-run_trigger (OstreeCheckout *self,
- GFile *trigger,
- gboolean requires_chroot,
- GError **error)
-{
- OstreeCheckoutPrivate *priv = GET_PRIVATE (self);
- gboolean ret = FALSE;
- char *path = NULL;
- char *temp_path = NULL;
- char *rel_temp_path = NULL;
- GFile *temp_copy = NULL;
- char *basename = NULL;
- GPtrArray *args = NULL;
- int estatus;
-
- path = g_file_get_path (trigger);
- basename = g_path_get_basename (path);
-
- args = g_ptr_array_new ();
-
- if (requires_chroot)
- {
- temp_path = g_build_filename (priv->path, basename, NULL);
- rel_temp_path = g_strconcat ("./", basename, NULL);
- temp_copy = ot_util_new_file_for_path (temp_path);
-
- if (!g_file_copy (trigger, temp_copy, 0, NULL, NULL, NULL, error))
- goto out;
-
- g_ptr_array_add (args, "chroot");
- g_ptr_array_add (args, ".");
- g_ptr_array_add (args, rel_temp_path);
- g_ptr_array_add (args, NULL);
- }
- else
- {
- g_ptr_array_add (args, path);
- g_ptr_array_add (args, NULL);
- }
-
- g_print ("Running trigger: %s\n", path);
- if (!g_spawn_sync (priv->path,
- (char**)args->pdata,
- NULL,
- G_SPAWN_SEARCH_PATH,
- NULL, NULL, NULL, NULL,
- &estatus,
- error))
- {
- g_prefix_error (error, "Failed to run trigger %s: ", basename);
- goto out;
- }
-
- ret = TRUE;
- out:
- if (requires_chroot && temp_path)
- (void)unlink (temp_path);
-
- g_free (path);
- g_free (basename);
- g_free (temp_path);
- g_free (rel_temp_path);
- g_clear_object (&temp_copy);
- if (args)
- g_ptr_array_free (args, TRUE);
- return ret;
-}
-
-static gboolean
-check_trigger (OstreeCheckout *self,
- GFile *trigger,
- GError **error)
-{
- OstreeCheckoutPrivate *priv = GET_PRIVATE (self);
- gboolean ret = FALSE;
- GInputStream *instream = NULL;
- GDataInputStream *datain = NULL;
- GError *temp_error = NULL;
- char *line;
- gsize len;
- gboolean requires_chroot = TRUE;
- gboolean matches = FALSE;
-
- instream = (GInputStream*)g_file_read (trigger, NULL, error);
- if (!instream)
- goto out;
- datain = g_data_input_stream_new (instream);
-
- while ((line = g_data_input_stream_read_line (datain, &len, NULL, &temp_error)) != NULL)
- {
- if (g_str_has_prefix (line, "# IfExecutable: "))
- {
- char *executable = g_strdup (line + strlen ("# IfExecutable: "));
- g_strchomp (executable);
- matches = executable_exists_in_checkout (priv->path, executable);
- g_free (executable);
- }
-
- g_free (line);
- }
- if (line == NULL && temp_error != NULL)
- {
- g_propagate_error (error, temp_error);
- goto out;
- }
- if (matches)
- {
- if (!run_trigger (self, trigger, requires_chroot, error))
- goto out;
- }
-
- ret = TRUE;
- out:
- g_clear_object (&instream);
- g_clear_object (&datain);
- return ret;
-}
-
-gboolean
-ostree_checkout_run_triggers (OstreeCheckout *self,
- GError **error)
-{
- gboolean ret = FALSE;
- GError *temp_error = NULL;
- char *triggerdir_path = NULL;
- GFile *triggerdir = NULL;
- GFileInfo *file_info = NULL;
- GFileEnumerator *enumerator = NULL;
-
- triggerdir_path = g_build_filename (LIBEXECDIR, "ostree", "triggers.d", NULL);
- triggerdir = ot_util_new_file_for_path (triggerdir_path);
-
- enumerator = g_file_enumerate_children (triggerdir, "standard::name,standard::type,unix::*",
- G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
- NULL,
- error);
- if (!enumerator)
- goto out;
-
- while ((file_info = g_file_enumerator_next_file (enumerator, NULL, &temp_error)) != NULL)
- {
- const char *name;
- guint32 type;
- char *child_path = NULL;
- GFile *child = NULL;
- gboolean success;
-
- name = g_file_info_get_attribute_byte_string (file_info, "standard::name");
- type = g_file_info_get_attribute_uint32 (file_info, "standard::type");
-
- if (type == G_FILE_TYPE_REGULAR && g_str_has_suffix (name, ".trigger"))
- {
- child_path = g_build_filename (triggerdir_path, name, NULL);
- child = ot_util_new_file_for_path (child_path);
-
- success = check_trigger (self, child, error);
- }
- else
- success = TRUE;
-
- g_object_unref (file_info);
- g_free (child_path);
- g_clear_object (&child);
- if (!success)
- goto out;
- }
- if (file_info == NULL && temp_error != NULL)
- {
- g_propagate_error (error, temp_error);
- goto out;
- }
-
- ret = TRUE;
- out:
- g_free (triggerdir_path);
- g_clear_object (&triggerdir);
- g_clear_object (&enumerator);
- return ret;
-}
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#ifndef _OSTREE_CHECKOUT
-#define _OSTREE_CHECKOUT
-
-#include <ostree-repo.h>
-
-G_BEGIN_DECLS
-
-#define OSTREE_TYPE_CHECKOUT ostree_checkout_get_type()
-#define OSTREE_CHECKOUT(obj) \
- (G_TYPE_CHECK_INSTANCE_CAST ((obj), OSTREE_TYPE_CHECKOUT, OstreeCheckout))
-#define OSTREE_CHECKOUT_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_CAST ((klass), OSTREE_TYPE_CHECKOUT, OstreeCheckoutClass))
-#define OSTREE_IS_CHECKOUT(obj) \
- (G_TYPE_CHECK_INSTANCE_TYPE ((obj), OSTREE_TYPE_CHECKOUT))
-#define OSTREE_IS_CHECKOUT_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_TYPE ((klass), OSTREE_TYPE_CHECKOUT))
-#define OSTREE_CHECKOUT_GET_CLASS(obj) \
- (G_TYPE_INSTANCE_GET_CLASS ((obj), OSTREE_TYPE_CHECKOUT, OstreeCheckoutClass))
-
-typedef struct {
- GObject parent;
-} OstreeCheckout;
-
-typedef struct {
- GObjectClass parent_class;
-} OstreeCheckoutClass;
-
-GType ostree_checkout_get_type (void);
-
-OstreeCheckout* ostree_checkout_new (OstreeRepo *repo,
- const char *path);
-
-gboolean ostree_checkout_run_triggers (OstreeCheckout *checkout,
- GError **error);
-
-G_END_DECLS
-
-#endif /* _OSTREE_CHECKOUT */
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ostree.h"
-#include "otutil.h"
-
-#include <sys/types.h>
-#include <attr/xattr.h>
-
-gboolean
-ostree_validate_checksum_string (const char *sha256,
- GError **error)
-{
- if (strlen (sha256) != 64)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Invalid rev '%s'", sha256);
- return FALSE;
- }
- return TRUE;
-}
-
-
-void
-ostree_checksum_update_stat (GChecksum *checksum, guint32 uid, guint32 gid, guint32 mode)
-{
- guint32 perms = (mode & ~S_IFMT);
- g_checksum_update (checksum, (guint8*) &uid, 4);
- g_checksum_update (checksum, (guint8*) &gid, 4);
- g_checksum_update (checksum, (guint8*) &perms, 4);
-}
-
-static char *
-canonicalize_xattrs (char *xattr_string, size_t len)
-{
- char *p;
- GSList *xattrs = NULL;
- GSList *iter;
- GString *result;
-
- result = g_string_new (0);
-
- p = xattr_string;
- while (p < xattr_string+len)
- {
- xattrs = g_slist_prepend (xattrs, p);
- p += strlen (p) + 1;
- }
-
- xattrs = g_slist_sort (xattrs, (GCompareFunc) strcmp);
- for (iter = xattrs; iter; iter = iter->next)
- g_string_append (result, iter->data);
-
- g_slist_free (xattrs);
- return g_string_free (result, FALSE);
-}
-
-static gboolean
-read_xattr_name_array (const char *path,
- const char *xattrs,
- size_t len,
- GVariantBuilder *builder,
- GError **error)
-{
- gboolean ret = FALSE;
- const char *p;
-
- p = xattrs;
- while (p < xattrs+len)
- {
- ssize_t bytes_read;
- char *buf;
-
- bytes_read = lgetxattr (path, p, NULL, 0);
- if (bytes_read < 0)
- {
- ot_util_set_error_from_errno (error, errno);
- goto out;
- }
- if (bytes_read == 0)
- continue;
-
- buf = g_malloc (bytes_read);
- if (lgetxattr (path, p, buf, bytes_read) < 0)
- {
- ot_util_set_error_from_errno (error, errno);
- g_free (buf);
- goto out;
- }
-
- g_variant_builder_add (builder, "(@ay@ay)",
- g_variant_new_bytestring (p),
- g_variant_new_from_data (G_VARIANT_TYPE ("ay"),
- buf, bytes_read, FALSE, g_free, buf));
-
- p = p + strlen (p) + 1;
- }
-
- ret = TRUE;
- out:
- return ret;
-}
-
-GVariant *
-ostree_get_xattrs_for_path (const char *path,
- GError **error)
-{
- GVariant *ret = NULL;
- GVariantBuilder builder;
- char *xattr_names = NULL;
- char *xattr_names_canonical = NULL;
- ssize_t bytes_read;
-
- g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ayay)"));
-
- bytes_read = llistxattr (path, NULL, 0);
-
- if (bytes_read < 0)
- {
- if (errno != ENOTSUP)
- {
- ot_util_set_error_from_errno (error, errno);
- goto out;
- }
- }
- else if (bytes_read > 0)
- {
- xattr_names = g_malloc (bytes_read);
- if (llistxattr (path, xattr_names, bytes_read) < 0)
- {
- ot_util_set_error_from_errno (error, errno);
- goto out;
- }
- xattr_names_canonical = canonicalize_xattrs (xattr_names, bytes_read);
-
- if (!read_xattr_name_array (path, xattr_names_canonical, bytes_read, &builder, error))
- goto out;
- }
-
- ret = g_variant_builder_end (&builder);
- g_variant_ref_sink (ret);
- out:
- if (!ret)
- g_variant_builder_clear (&builder);
- g_free (xattr_names);
- g_free (xattr_names_canonical);
- return ret;
-}
-
-gboolean
-ostree_stat_and_checksum_file (int dir_fd, const char *path,
- OstreeObjectType objtype,
- GChecksum **out_checksum,
- struct stat *out_stbuf,
- GError **error)
-{
- GChecksum *content_sha256 = NULL;
- GChecksum *content_and_meta_sha256 = NULL;
- char *stat_string = NULL;
- ssize_t bytes_read;
- GVariant *xattrs = NULL;
- int fd = -1;
- DIR *temp_dir = NULL;
- char *basename = NULL;
- gboolean ret = FALSE;
- char *symlink_target = NULL;
- char *device_id = NULL;
- struct stat stbuf;
-
- basename = g_path_get_basename (path);
-
- if (dir_fd == -1)
- {
- char *dirname = g_path_get_dirname (path);
- temp_dir = opendir (dirname);
- if (temp_dir == NULL)
- {
- ot_util_set_error_from_errno (error, errno);
- g_free (dirname);
- }
- g_free (dirname);
- dir_fd = dirfd (temp_dir);
- }
-
- if (fstatat (dir_fd, basename, &stbuf, AT_SYMLINK_NOFOLLOW) < 0)
- {
- ot_util_set_error_from_errno (error, errno);
- goto out;
- }
-
- if (S_ISREG(stbuf.st_mode))
- {
- fd = ot_util_open_file_read_at (dir_fd, basename, error);
- if (fd < 0)
- {
- ot_util_set_error_from_errno (error, errno);
- goto out;
- }
- }
-
- if (objtype == OSTREE_OBJECT_TYPE_FILE)
- {
- xattrs = ostree_get_xattrs_for_path (path, error);
- if (!xattrs)
- goto out;
- }
-
- content_sha256 = g_checksum_new (G_CHECKSUM_SHA256);
-
- if (S_ISREG(stbuf.st_mode))
- {
- guint8 buf[8192];
-
- while ((bytes_read = read (fd, buf, sizeof (buf))) > 0)
- g_checksum_update (content_sha256, buf, bytes_read);
- if (bytes_read < 0)
- {
- ot_util_set_error_from_errno (error, errno);
- goto out;
- }
- }
- else if (S_ISLNK(stbuf.st_mode))
- {
- symlink_target = g_malloc (PATH_MAX);
-
- g_assert (objtype == OSTREE_OBJECT_TYPE_FILE);
-
- bytes_read = readlinkat (dir_fd, basename, symlink_target, PATH_MAX);
- if (bytes_read < 0)
- {
- ot_util_set_error_from_errno (error, errno);
- goto out;
- }
- g_checksum_update (content_sha256, (guint8*)symlink_target, bytes_read);
- }
- else if (S_ISCHR(stbuf.st_mode) || S_ISBLK(stbuf.st_mode))
- {
- g_assert (objtype == OSTREE_OBJECT_TYPE_FILE);
- device_id = g_strdup_printf ("%u", (guint)stbuf.st_rdev);
- g_checksum_update (content_sha256, (guint8*)device_id, strlen (device_id));
- }
- else
- {
- g_set_error (error, G_IO_ERROR,
- G_IO_ERROR_FAILED,
- "Unsupported file '%s' (must be regular, symbolic link, or device)",
- path);
- goto out;
- }
-
- content_and_meta_sha256 = g_checksum_copy (content_sha256);
-
- if (objtype == OSTREE_OBJECT_TYPE_FILE)
- {
- ostree_checksum_update_stat (content_and_meta_sha256, stbuf.st_uid,
- stbuf.st_gid, stbuf.st_mode);
- g_checksum_update (content_and_meta_sha256, (guint8*)g_variant_get_data (xattrs), g_variant_get_size (xattrs));
- }
-
- *out_stbuf = stbuf;
- *out_checksum = content_and_meta_sha256;
- ret = TRUE;
- out:
- if (fd >= 0)
- close (fd);
- if (temp_dir != NULL)
- closedir (temp_dir);
- g_free (symlink_target);
- g_free (basename);
- g_free (stat_string);
- if (xattrs)
- g_variant_unref (xattrs);
- if (content_sha256)
- g_checksum_free (content_sha256);
- return ret;
-}
-
-gboolean
-ostree_set_xattrs (const char *path, GVariant *xattrs, GError **error)
-{
- gboolean ret = FALSE;
- int i, n;
-
- n = g_variant_n_children (xattrs);
- for (i = 0; i < n; i++)
- {
- const guint8* name;
- GVariant *value;
- const guint8* value_data;
- gsize value_len;
- gboolean loop_err;
-
- g_variant_get_child (xattrs, i, "(^&ay@ay)",
- &name, &value);
- value_data = g_variant_get_fixed_array (value, &value_len, 1);
-
- loop_err = lsetxattr (path, (char*)name, (char*)value_data, value_len, XATTR_REPLACE) < 0;
-
- g_variant_unref (value);
- if (loop_err)
- {
- ot_util_set_error_from_errno (error, errno);
- goto out;
- }
- }
-
- ret = TRUE;
- out:
- return ret;
-}
-
-gboolean
-ostree_parse_metadata_file (const char *path,
- OstreeSerializedVariantType *out_type,
- GVariant **out_variant,
- GError **error)
-{
- GMappedFile *mfile = NULL;
- gboolean ret = FALSE;
- GVariant *ret_variant = NULL;
- GVariant *container = NULL;
- guint32 ret_type;
-
- mfile = g_mapped_file_new (path, FALSE, error);
- if (mfile == NULL)
- {
- goto out;
- }
- else
- {
- container = g_variant_new_from_data (G_VARIANT_TYPE (OSTREE_SERIALIZED_VARIANT_FORMAT),
- g_mapped_file_get_contents (mfile),
- g_mapped_file_get_length (mfile),
- FALSE,
- (GDestroyNotify) g_mapped_file_unref,
- mfile);
- mfile = NULL;
- g_variant_ref_sink (container);
- g_variant_get (container, "(uv)",
- &ret_type, &ret_variant);
- ret_type = GUINT32_FROM_BE (ret_type);
- if (ret_type <= 0 || ret_type > OSTREE_SERIALIZED_VARIANT_LAST)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Corrupted metadata object '%s'; invalid type %d", path, ret_type);
- goto out;
- }
- }
-
- ret = TRUE;
- *out_type = ret_type;
- *out_variant = g_variant_ref_sink (ret_variant);
- ret_variant = NULL;
- out:
- if (ret_variant)
- g_variant_unref (ret_variant);
- if (container != NULL)
- g_variant_unref (container);
- if (mfile != NULL)
- g_mapped_file_unref (mfile);
- return ret;
-}
-
-char *
-ostree_get_relative_object_path (const char *checksum,
- OstreeObjectType type,
- gboolean archive)
-{
- GString *path;
- const char *type_string;
-
- g_assert (strlen (checksum) == 64);
-
- path = g_string_new ("objects/");
-
- g_string_append_len (path, checksum, 2);
- g_string_append_c (path, '/');
- g_string_append (path, checksum + 2);
- switch (type)
- {
- case OSTREE_OBJECT_TYPE_FILE:
- if (archive)
- type_string = ".packfile";
- else
- type_string = ".file";
- break;
- case OSTREE_OBJECT_TYPE_META:
- type_string = ".meta";
- break;
- default:
- g_assert_not_reached ();
- }
- g_string_append (path, type_string);
- return g_string_free (path, FALSE);
-}
-
-gboolean
-ostree_pack_object (GOutputStream *output,
- GFile *file,
- OstreeObjectType objtype,
- GCancellable *cancellable,
- GError **error)
-{
- gboolean ret = FALSE;
- char *path = NULL;
- GFileInfo *finfo = NULL;
- GFileInputStream *instream = NULL;
- gboolean pack_builder_initialized = FALSE;
- GVariantBuilder pack_builder;
- GVariant *pack_variant = NULL;
- GVariant *xattrs = NULL;
- gsize bytes_written;
-
- path = g_file_get_path (file);
-
- finfo = g_file_query_info (file, "standard::type,standard::size,standard::is-symlink,standard::symlink-target,unix::*",
- G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, cancellable, error);
- if (!finfo)
- goto out;
-
- if (objtype == OSTREE_OBJECT_TYPE_META)
- {
- guint64 object_size_be = GUINT64_TO_BE ((guint64)g_file_info_get_size (finfo));
- if (!g_output_stream_write_all (output, &object_size_be, 8, &bytes_written, cancellable, error))
- goto out;
-
- instream = g_file_read (file, NULL, error);
- if (!instream)
- goto out;
-
- if (g_output_stream_splice (output, (GInputStream*)instream, 0, cancellable, error) < 0)
- goto out;
- }
- else
- {
- guint32 uid, gid, mode;
- guint32 device = 0;
- guint32 metadata_size_be;
- const char *target = NULL;
- guint64 object_size;
-
- uid = g_file_info_get_attribute_uint32 (finfo, G_FILE_ATTRIBUTE_UNIX_UID);
- gid = g_file_info_get_attribute_uint32 (finfo, G_FILE_ATTRIBUTE_UNIX_GID);
- mode = g_file_info_get_attribute_uint32 (finfo, G_FILE_ATTRIBUTE_UNIX_MODE);
-
- g_variant_builder_init (&pack_builder, G_VARIANT_TYPE (OSTREE_PACK_FILE_VARIANT_FORMAT));
- pack_builder_initialized = TRUE;
- g_variant_builder_add (&pack_builder, "u", GUINT32_TO_BE (0));
- g_variant_builder_add (&pack_builder, "u", GUINT32_TO_BE (uid));
- g_variant_builder_add (&pack_builder, "u", GUINT32_TO_BE (gid));
- g_variant_builder_add (&pack_builder, "u", GUINT32_TO_BE (mode));
-
- xattrs = ostree_get_xattrs_for_path (path, error);
- if (!xattrs)
- goto out;
- g_variant_builder_add (&pack_builder, "@a(ayay)", xattrs);
-
- if (S_ISREG (mode))
- {
- object_size = (guint64)g_file_info_get_size (finfo);
- }
- else if (S_ISLNK (mode))
- {
- target = g_file_info_get_attribute_byte_string (finfo, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET);
- object_size = strlen (target);
- }
- else if (S_ISBLK (mode) || S_ISCHR (mode))
- {
- device = g_file_info_get_attribute_uint32 (finfo, G_FILE_ATTRIBUTE_UNIX_DEVICE);
- object_size = 4;
- }
- else
- g_assert_not_reached ();
-
- g_variant_builder_add (&pack_builder, "t", GUINT64_TO_BE (object_size));
- pack_variant = g_variant_builder_end (&pack_builder);
- pack_builder_initialized = FALSE;
-
- metadata_size_be = GUINT32_TO_BE (g_variant_get_size (pack_variant));
-
- if (!g_output_stream_write_all (output, &metadata_size_be, 4,
- &bytes_written, cancellable, error))
- goto out;
- g_assert (bytes_written == 4);
-
- if (!g_output_stream_write_all (output, g_variant_get_data (pack_variant), g_variant_get_size (pack_variant),
- &bytes_written, cancellable, error))
- goto out;
-
- if (S_ISREG (mode))
- {
- instream = g_file_read (file, NULL, error);
- if (!instream)
- goto out;
- bytes_written = g_output_stream_splice (output, (GInputStream*)instream, 0, cancellable, error);
- if (bytes_written < 0)
- goto out;
- if (bytes_written != object_size)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "File size changed unexpectedly");
- goto out;
- }
- }
- else if (S_ISLNK (mode))
- {
- if (!g_output_stream_write_all (output, target, object_size,
- &bytes_written, cancellable, error))
- goto out;
- }
- else if (S_ISBLK (mode) || S_ISCHR (mode))
- {
- guint32 device_be = GUINT32_TO_BE (device);
- g_assert (object_size == 4);
- if (!g_output_stream_write_all (output, &device_be, object_size,
- &bytes_written, cancellable, error))
- goto out;
- g_assert (bytes_written == 4);
- }
- else
- g_assert_not_reached ();
- }
-
- ret = TRUE;
- out:
- g_free (path);
- g_clear_object (&finfo);
- g_clear_object (&instream);
- if (xattrs)
- g_variant_unref (xattrs);
- if (pack_builder_initialized)
- g_variant_builder_clear (&pack_builder);
- if (pack_variant)
- g_variant_unref (pack_variant);
- return ret;
-}
-
-static gboolean
-splice_and_checksum (GOutputStream *out,
- GInputStream *in,
- GChecksum *checksum,
- GCancellable *cancellable,
- GError **error)
-{
- gboolean ret = FALSE;
-
- if (checksum != NULL)
- {
- gsize bytes_read, bytes_written;
- char buf[4096];
- do
- {
- if (!g_input_stream_read_all (in, buf, sizeof(buf), &bytes_read, cancellable, error))
- goto out;
- if (checksum)
- g_checksum_update (checksum, (guint8*)buf, bytes_read);
- if (!g_output_stream_write_all (out, buf, bytes_read, &bytes_written, cancellable, error))
- goto out;
- }
- while (bytes_read > 0);
- }
- else
- {
- if (g_output_stream_splice (out, in, 0, cancellable, error) < 0)
- goto out;
- }
-
- ret = TRUE;
- out:
- return ret;
-}
-
-static gboolean
-unpack_meta (const char *path,
- const char *dest_path,
- GChecksum **out_checksum,
- GError **error)
-{
- gboolean ret = FALSE;
- GFile *file = NULL;
- GFile *dest_file = NULL;
- GFileInputStream *in = NULL;
- GChecksum *ret_checksum = NULL;
- GFileOutputStream *out = NULL;
-
- file = ot_util_new_file_for_path (path);
- dest_file = ot_util_new_file_for_path (dest_path);
-
- if (out_checksum)
- ret_checksum = g_checksum_new (G_CHECKSUM_SHA256);
-
- in = g_file_read (file, NULL, error);
- if (!in)
- goto out;
-
- out = g_file_replace (dest_file, NULL, FALSE, 0, NULL, error);
- if (!out)
- goto out;
-
- if (!splice_and_checksum ((GOutputStream*)out, (GInputStream*)in, ret_checksum, NULL, error))
- goto out;
-
- if (!g_output_stream_close ((GOutputStream*)out, NULL, error))
- goto out;
-
- ret = TRUE;
- if (out_checksum)
- *out_checksum = ret_checksum;
- ret_checksum = NULL;
- out:
- if (!ret)
- (void) unlink (dest_path);
- if (ret_checksum)
- g_checksum_free (ret_checksum);
- g_clear_object (&file);
- g_clear_object (&dest_file);
- g_clear_object (&in);
- return ret;
-}
-
-
-static gboolean
-unpack_file (const char *path,
- const char *dest_path,
- GChecksum **out_checksum,
- GError **error)
-{
- gboolean ret = FALSE;
- GFile *file = NULL;
- GFile *dest_file = NULL;
- char *metadata_buf = NULL;
- GVariant *metadata = NULL;
- GVariant *xattrs = NULL;
- GFileInputStream *in = NULL;
- GFileOutputStream *out = NULL;
- GChecksum *ret_checksum = NULL;
- guint32 metadata_len;
- guint32 version, uid, gid, mode;
- guint64 content_len;
- gsize bytes_read, bytes_written;
- int temp_fd = -1;
-
- file = ot_util_new_file_for_path (path);
-
- in = g_file_read (file, NULL, error);
- if (!in)
- goto out;
-
- if (!g_input_stream_read_all ((GInputStream*)in, &metadata_len, 4, &bytes_read, NULL, error))
- goto out;
- if (bytes_read != 4)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Corrupted packfile; too short while reading metadata length");
- goto out;
- }
-
- metadata_len = GUINT32_FROM_BE (metadata_len);
- metadata_buf = g_malloc (metadata_len);
-
- if (!g_input_stream_read_all ((GInputStream*)in, metadata_buf, metadata_len, &bytes_read, NULL, error))
- goto out;
- if (bytes_read != metadata_len)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Corrupted packfile; too short while reading metadata");
- goto out;
- }
-
- metadata = g_variant_new_from_data (G_VARIANT_TYPE (OSTREE_PACK_FILE_VARIANT_FORMAT),
- metadata_buf, metadata_len, FALSE, NULL, NULL);
-
- g_variant_get (metadata, "(uuuu@a(ayay)t)",
- &version, &uid, &gid, &mode,
- &xattrs, &content_len);
- uid = GUINT32_FROM_BE (uid);
- gid = GUINT32_FROM_BE (gid);
- mode = GUINT32_FROM_BE (mode);
- content_len = GUINT64_FROM_BE (content_len);
-
- dest_file = ot_util_new_file_for_path (dest_path);
-
- if (out_checksum)
- ret_checksum = g_checksum_new (G_CHECKSUM_SHA256);
-
- if (S_ISREG (mode))
- {
- out = g_file_replace (dest_file, NULL, FALSE, 0, NULL, error);
- if (!out)
- goto out;
-
- if (!splice_and_checksum ((GOutputStream*)out, (GInputStream*)in, ret_checksum, NULL, error))
- goto out;
-
- if (!g_output_stream_close ((GOutputStream*)out, NULL, error))
- goto out;
- }
- else if (S_ISLNK (mode))
- {
- char target[PATH_MAX+1];
-
- if (!g_input_stream_read_all ((GInputStream*)in, target, sizeof(target)-1, &bytes_read, NULL, error))
- goto out;
- target[bytes_read] = '\0';
- if (ret_checksum)
- g_checksum_update (ret_checksum, (guint8*)target, bytes_read);
- if (symlink (target, dest_path) < 0)
- {
- ot_util_set_error_from_errno (error, errno);
- goto out;
- }
- }
- else if (S_ISCHR (mode) || S_ISBLK (mode))
- {
- guint32 dev;
-
- if (!g_input_stream_read_all ((GInputStream*)in, &dev, 4, &bytes_read, NULL, error))
- goto out;
- if (bytes_read != 4)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Corrupted packfile; too short while reading device id");
- goto out;
- }
- dev = GUINT32_FROM_BE (dev);
- if (ret_checksum)
- g_checksum_update (ret_checksum, (guint8*)&dev, 4);
- if (mknod (dest_path, mode, dev) < 0)
- {
- ot_util_set_error_from_errno (error, errno);
- goto out;
- }
- }
- else
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Corrupted packfile; invalid mode %u", mode);
- goto out;
- }
-
- if (!S_ISLNK (mode))
- {
- if (chmod (dest_path, mode) < 0)
- {
- ot_util_set_error_from_errno (error, errno);
- goto out;
- }
- }
-
- if (!ostree_set_xattrs (dest_path, xattrs, error))
- goto out;
-
- if (ret_checksum)
- {
- ostree_checksum_update_stat (ret_checksum, uid, gid, mode);
- g_checksum_update (ret_checksum, (guint8*)g_variant_get_data (xattrs), g_variant_get_size (xattrs));
- }
-
- ret = TRUE;
- if (out_checksum)
- *out_checksum = ret_checksum;
- ret_checksum = NULL;
- out:
- if (!ret)
- (void) unlink (dest_path);
- if (ret_checksum)
- g_checksum_free (ret_checksum);
- g_free (metadata_buf);
- g_clear_object (&file);
- g_clear_object (&dest_file);
- g_clear_object (&in);
- g_clear_object (&out);
- if (metadata)
- g_variant_unref (metadata);
- if (xattrs)
- g_variant_unref (xattrs);
- return ret;
-}
-
-gboolean
-ostree_unpack_object (const char *path,
- OstreeObjectType objtype,
- const char *dest_path,
- GChecksum **out_checksum,
- GError **error)
-{
- if (objtype == OSTREE_OBJECT_TYPE_META)
- return unpack_meta (path, dest_path, out_checksum, error);
- else
- return unpack_file (path, dest_path, out_checksum, error);
-}
-
-
-
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#ifndef _OSTREE_CORE
-#define _OSTREE_CORE
-
-#include <otutil.h>
-
-G_BEGIN_DECLS
-
-#define OSTREE_EMPTY_STRING_SHA256 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
-
-typedef enum {
- OSTREE_OBJECT_TYPE_FILE = 1,
- OSTREE_OBJECT_TYPE_META = 2,
-} OstreeObjectType;
-
-typedef enum {
- OSTREE_SERIALIZED_TREE_VARIANT = 1,
- OSTREE_SERIALIZED_COMMIT_VARIANT = 2,
- OSTREE_SERIALIZED_DIRMETA_VARIANT = 3,
- OSTREE_SERIALIZED_XATTR_VARIANT = 4
-} OstreeSerializedVariantType;
-#define OSTREE_SERIALIZED_VARIANT_LAST 4
-
-#define OSTREE_SERIALIZED_VARIANT_FORMAT "(uv)"
-
-/*
- * xattr objects:
- * a(ayay) - array of (name, value) pairs, both binary data, though name is a bytestring
- */
-#define OSTREE_XATTR_GVARIANT_FORMAT "a(ayay)"
-
-#define OSTREE_DIR_META_VERSION 0
-/*
- * dirmeta objects:
- * u - Version
- * u - uid
- * u - gid
- * u - mode
- * a(ayay) - xattrs
- */
-#define OSTREE_DIRMETA_GVARIANT_FORMAT "(uuuua(ayay))"
-
-#define OSTREE_TREE_VERSION 0
-/*
- * Tree objects:
- * u - Version
- * a{sv} - Metadata
- * a(ss) - array of (filename, checksum) for files
- * a(sss) - array of (dirname, tree_checksum, meta_checksum) for directories
- */
-#define OSTREE_TREE_GVARIANT_FORMAT "(ua{sv}a(ss)a(sss)"
-
-#define OSTREE_COMMIT_VERSION 0
-/*
- * Commit objects:
- * u - Version
- * a{sv} - Metadata
- * s - parent checksum (empty string for initial)
- * s - subject
- * s - body
- * t - Timestamp in seconds since the epoch (UTC)
- * s - Root tree contents
- * s - Root tree metadata
- */
-#define OSTREE_COMMIT_GVARIANT_FORMAT "(ua{sv}ssstss)"
-
-gboolean ostree_validate_checksum_string (const char *sha256,
- GError **error);
-
-char *ostree_get_relative_object_path (const char *checksum,
- OstreeObjectType type,
- gboolean archive);
-
-GVariant *ostree_get_xattrs_for_path (const char *path,
- GError **error);
-
-gboolean ostree_set_xattrs (const char *path, GVariant *xattrs, GError **error);
-
-gboolean ostree_parse_metadata_file (const char *path,
- OstreeSerializedVariantType *out_type,
- GVariant **out_variant,
- GError **error);
-
-gboolean ostree_stat_and_checksum_file (int dirfd, const char *path,
- OstreeObjectType type,
- GChecksum **out_checksum,
- struct stat *out_stbuf,
- GError **error);
-
-/* Packed files:
- *
- * guint32 metadata_length [metadata gvariant] [content]
- *
- * metadata variant:
- * u - Version
- * u - uid
- * u - gid
- * u - mode
- * a(ayay) - xattrs
- * t - content length
- *
- * And then following the end of the variant is the content. If
- * symlink, then this is the target; if device, then device ID as
- * network byte order uint32.
- */
-#define OSTREE_PACK_FILE_VARIANT_FORMAT "(uuuua(ayay)t)"
-
-gboolean ostree_pack_object (GOutputStream *output,
- GFile *path,
- OstreeObjectType objtype,
- GCancellable *cancellable,
- GError **error);
-
-gboolean ostree_unpack_object (const char *path,
- OstreeObjectType objtype,
- const char *dest_path,
- GChecksum **out_checksum,
- GError **error);
-
-void ostree_checksum_update_stat (GChecksum *checksum, guint32 uid, guint32 gid, guint32 mode);
-
-
-#endif /* _OSTREE_REPO */
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#define _GNU_SOURCE
-
-#include "config.h"
-
-#include "ostree.h"
-#include "otutil.h"
-
-#include <gio/gunixoutputstream.h>
-#include <gio/gunixinputstream.h>
-
-static gboolean
-link_one_file (OstreeRepo *self, const char *path,
- OstreeObjectType type,
- gboolean ignore_exists, gboolean force,
- GChecksum **out_checksum,
- GError **error);
-static char *
-get_object_path (OstreeRepo *self,
- const char *checksum,
- OstreeObjectType type);
-
-enum {
- PROP_0,
-
- PROP_PATH
-};
-
-G_DEFINE_TYPE (OstreeRepo, ostree_repo, G_TYPE_OBJECT)
-
-#define GET_PRIVATE(o) \
- (G_TYPE_INSTANCE_GET_PRIVATE ((o), OSTREE_TYPE_REPO, OstreeRepoPrivate))
-
-typedef struct _OstreeRepoPrivate OstreeRepoPrivate;
-
-struct _OstreeRepoPrivate {
- char *path;
- GFile *repo_file;
- GFile *local_heads_dir;
- GFile *remote_heads_dir;
- char *objects_path;
- char *config_path;
-
- gboolean inited;
-
- GKeyFile *config;
- gboolean archive;
-};
-
-static void
-ostree_repo_finalize (GObject *object)
-{
- OstreeRepo *self = OSTREE_REPO (object);
- OstreeRepoPrivate *priv = GET_PRIVATE (self);
-
- g_free (priv->path);
- g_clear_object (&priv->repo_file);
- g_clear_object (&priv->local_heads_dir);
- g_clear_object (&priv->remote_heads_dir);
- g_free (priv->objects_path);
- g_free (priv->config_path);
- if (priv->config)
- g_key_file_free (priv->config);
-
- G_OBJECT_CLASS (ostree_repo_parent_class)->finalize (object);
-}
-
-static void
-ostree_repo_set_property(GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- OstreeRepo *self = OSTREE_REPO (object);
- OstreeRepoPrivate *priv = GET_PRIVATE (self);
-
- switch (prop_id)
- {
- case PROP_PATH:
- priv->path = g_value_dup_string (value);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static void
-ostree_repo_get_property(GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec)
-{
- OstreeRepo *self = OSTREE_REPO (object);
- OstreeRepoPrivate *priv = GET_PRIVATE (self);
-
- switch (prop_id)
- {
- case PROP_PATH:
- g_value_set_string (value, priv->path);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static GObject *
-ostree_repo_constructor (GType gtype,
- guint n_properties,
- GObjectConstructParam *properties)
-{
- GObject *object;
- GObjectClass *parent_class;
- OstreeRepoPrivate *priv;
-
- parent_class = G_OBJECT_CLASS (ostree_repo_parent_class);
- object = parent_class->constructor (gtype, n_properties, properties);
-
- priv = GET_PRIVATE (object);
-
- g_assert (priv->path != NULL);
-
- priv->repo_file = ot_util_new_file_for_path (priv->path);
- priv->local_heads_dir = g_file_resolve_relative_path (priv->repo_file, "refs/heads");
- priv->remote_heads_dir = g_file_resolve_relative_path (priv->repo_file, "refs/remotes");
-
- priv->objects_path = g_build_filename (priv->path, "objects", NULL);
- priv->config_path = g_build_filename (priv->path, "config", NULL);
-
- return object;
-}
-
-static void
-ostree_repo_class_init (OstreeRepoClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
- g_type_class_add_private (klass, sizeof (OstreeRepoPrivate));
-
- object_class->constructor = ostree_repo_constructor;
- object_class->get_property = ostree_repo_get_property;
- object_class->set_property = ostree_repo_set_property;
- object_class->finalize = ostree_repo_finalize;
-
- g_object_class_install_property (object_class,
- PROP_PATH,
- g_param_spec_string ("path",
- "",
- "",
- NULL,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-}
-
-static void
-ostree_repo_init (OstreeRepo *self)
-{
-}
-
-OstreeRepo*
-ostree_repo_new (const char *path)
-{
- return g_object_new (OSTREE_TYPE_REPO, "path", path, NULL);
-}
-
-static gboolean
-parse_rev_file (OstreeRepo *self,
- const char *path,
- char **sha256,
- GError **error) G_GNUC_UNUSED;
-
-static gboolean
-parse_rev_file (OstreeRepo *self,
- const char *path,
- char **sha256,
- GError **error)
-{
- OstreeRepoPrivate *priv = GET_PRIVATE (self);
- GError *temp_error = NULL;
- gboolean ret = FALSE;
- char *rev = NULL;
-
- rev = ot_util_get_file_contents_utf8 (path, &temp_error);
- if (rev == NULL)
- {
- if (g_error_matches (temp_error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
- {
- g_clear_error (&temp_error);
- }
- else
- {
- g_propagate_error (error, temp_error);
- goto out;
- }
- }
- else
- {
- g_strchomp (rev);
- }
-
- if (g_str_has_prefix (rev, "ref: "))
- {
- GFile *ref;
- char *ref_path;
- char *ref_sha256;
- gboolean subret;
-
- ref = g_file_resolve_relative_path (priv->local_heads_dir, rev + 5);
- ref_path = g_file_get_path (ref);
-
- subret = parse_rev_file (self, ref_path, &ref_sha256, error);
- g_clear_object (&ref);
- g_free (ref_path);
-
- if (!subret)
- {
- g_free (ref_sha256);
- goto out;
- }
-
- g_free (rev);
- rev = ref_sha256;
- }
- else
- {
- if (!ostree_validate_checksum_string (rev, error))
- goto out;
- }
-
- *sha256 = rev;
- rev = NULL;
- ret = TRUE;
- out:
- g_free (rev);
- return ret;
-}
-
-static gboolean
-resolve_rev (OstreeRepo *self,
- const char *rev,
- gboolean allow_noent,
- char **sha256,
- GError **error)
-{
- OstreeRepoPrivate *priv = GET_PRIVATE (self);
- gboolean ret = FALSE;
- char *tmp = NULL;
- char *tmp2 = NULL;
- char *ret_rev = NULL;
- GFile *child = NULL;
- char *child_path = NULL;
- GError *temp_error = NULL;
- GVariant *commit = NULL;
-
- if (strlen (rev) == 0)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Invalid empty rev");
- goto out;
- }
- else if (strlen (rev) == 64)
- {
- ret_rev = g_strdup (rev);
- }
- else if (g_str_has_suffix (rev, "^"))
- {
- tmp = g_strdup (rev);
- tmp[strlen(tmp) - 1] = '\0';
-
- if (!resolve_rev (self, tmp, allow_noent, &tmp2, error))
- goto out;
-
- if (!ostree_repo_load_variant_checked (self, OSTREE_SERIALIZED_COMMIT_VARIANT, tmp2, &commit, error))
- goto out;
-
- g_variant_get_child (commit, 2, "s", &ret_rev);
- if (strlen (ret_rev) == 0)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Commit %s has no parent", tmp2);
- goto out;
-
- }
- }
- else
- {
- child = g_file_get_child (priv->local_heads_dir, rev);
- child_path = g_file_get_path (child);
- if (!ot_util_gfile_load_contents_utf8 (child, NULL, &ret_rev, NULL, &temp_error))
- {
- if (allow_noent && g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
- {
- g_free (ret_rev);
- ret_rev = NULL;
- }
- else
- {
- g_propagate_error (error, temp_error);
- g_prefix_error (error, "Couldn't open ref '%s': ", child_path);
- goto out;
- }
- }
- else
- {
- g_strchomp (ret_rev);
-
- if (!ostree_validate_checksum_string (ret_rev, error))
- goto out;
- }
- }
-
- *sha256 = ret_rev;
- ret_rev = NULL;
- ret = TRUE;
- out:
- if (commit)
- g_variant_unref (commit);
- g_free (tmp);
- g_free (tmp2);
- g_clear_object (&child);
- g_free (child_path);
- g_free (ret_rev);
- return ret;
-}
-
-gboolean
-ostree_repo_resolve_rev (OstreeRepo *self,
- const char *rev,
- char **sha256,
- GError **error)
-{
- return resolve_rev (self, rev, FALSE, sha256, error);
-}
-
-static gboolean
-write_checksum_file (GFile *parentdir,
- const char *name,
- const char *sha256,
- GError **error)
-{
- gboolean ret = FALSE;
- GFile *child = NULL;
- GOutputStream *out = NULL;
- gsize bytes_written;
-
- child = g_file_get_child (parentdir, name);
-
- if ((out = (GOutputStream*)g_file_replace (child, NULL, FALSE, 0, NULL, error)) == NULL)
- goto out;
- if (!g_output_stream_write_all (out, sha256, strlen (sha256), &bytes_written, NULL, error))
- goto out;
- if (!g_output_stream_write_all (out, "\n", 1, &bytes_written, NULL, error))
- goto out;
- if (!g_output_stream_close (out, NULL, error))
- goto out;
-
- ret = TRUE;
- out:
- g_clear_object (&child);
- g_clear_object (&out);
- return ret;
-}
-
-/**
- * ostree_repo_get_config:
- * @self:
- *
- * Returns: (transfer none): The repository configuration; do not modify
- */
-GKeyFile *
-ostree_repo_get_config (OstreeRepo *self)
-{
- OstreeRepoPrivate *priv = GET_PRIVATE (self);
-
- g_return_val_if_fail (priv->inited, NULL);
-
- return priv->config;
-}
-
-/**
- * ostree_repo_copy_config:
- * @self:
- *
- * Returns: (transfer full): A newly-allocated copy of the repository config
- */
-GKeyFile *
-ostree_repo_copy_config (OstreeRepo *self)
-{
- OstreeRepoPrivate *priv = GET_PRIVATE (self);
- GKeyFile *copy;
- char *data;
- gsize len;
-
- g_return_val_if_fail (priv->inited, NULL);
-
- copy = g_key_file_new ();
- data = g_key_file_to_data (priv->config, &len, NULL);
- if (!g_key_file_load_from_data (copy, data, len, 0, NULL))
- g_assert_not_reached ();
- g_free (data);
- return copy;
-}
-
-/**
- * ostree_repo_write_config:
- * @self:
- * @new_config: Overwrite the config file with this data. Do not change later!
- * @error: a #GError
- *
- * Save @new_config in place of this repository's config file. Note
- * that @new_config should not be modified after - this function
- * simply adds a reference.
- */
-gboolean
-ostree_repo_write_config (OstreeRepo *self,
- GKeyFile *new_config,
- GError **error)
-{
- OstreeRepoPrivate *priv = GET_PRIVATE (self);
- char *data = NULL;
- gsize len;
- gboolean ret = FALSE;
-
- g_return_val_if_fail (priv->inited, FALSE);
-
- data = g_key_file_to_data (new_config, &len, error);
- if (!g_file_set_contents (priv->config_path, data, len, error))
- goto out;
-
- g_key_file_unref (priv->config);
- priv->config = g_key_file_ref (new_config);
-
- ret = TRUE;
- out:
- g_free (data);
- return ret;
-}
-
-gboolean
-ostree_repo_check (OstreeRepo *self, GError **error)
-{
- OstreeRepoPrivate *priv = GET_PRIVATE (self);
- gboolean ret = FALSE;
- char *version = NULL;;
- GError *temp_error = NULL;
-
- g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
-
- if (priv->inited)
- return TRUE;
-
- if (!g_file_test (priv->objects_path, G_FILE_TEST_IS_DIR))
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Couldn't find objects directory '%s'", priv->objects_path);
- goto out;
- }
-
- priv->config = g_key_file_new ();
- if (!g_key_file_load_from_file (priv->config, priv->config_path, 0, error))
- {
- g_prefix_error (error, "Couldn't parse config file: ");
- goto out;
- }
-
- version = g_key_file_get_value (priv->config, "core", "repo_version", &temp_error);
- if (temp_error)
- {
- g_propagate_error (error, temp_error);
- goto out;
- }
-
- if (strcmp (version, "0") != 0)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Invalid repository version '%s'", version);
- goto out;
- }
-
- priv->archive = g_key_file_get_boolean (priv->config, "core", "archive", &temp_error);
- if (temp_error)
- {
- if (g_error_matches (temp_error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND))
- {
- g_clear_error (&temp_error);
- }
- else
- {
- g_propagate_error (error, temp_error);
- goto out;
- }
- }
-
- priv->inited = TRUE;
-
- ret = TRUE;
- out:
- g_free (version);
- return ret;
-}
-
-const char *
-ostree_repo_get_path (OstreeRepo *self)
-{
- OstreeRepoPrivate *priv = GET_PRIVATE (self);
- return priv->path;
-}
-
-gboolean
-ostree_repo_is_archive (OstreeRepo *self)
-{
- OstreeRepoPrivate *priv = GET_PRIVATE (self);
-
- g_return_val_if_fail (priv->inited, FALSE);
-
- return priv->archive;
-}
-
-static gboolean
-import_gvariant_object (OstreeRepo *self,
- OstreeSerializedVariantType type,
- GVariant *variant,
- GChecksum **out_checksum,
- GError **error)
-{
- OstreeRepoPrivate *priv = GET_PRIVATE (self);
- GVariant *serialized = NULL;
- gboolean ret = FALSE;
- gsize bytes_written;
- char *tmp_name = NULL;
- int fd = -1;
- GUnixOutputStream *stream = NULL;
-
- serialized = g_variant_new ("(uv)", GUINT32_TO_BE ((guint32)type), variant);
-
- tmp_name = g_build_filename (priv->objects_path, "variant-tmp-XXXXXX", NULL);
- fd = g_mkstemp (tmp_name);
- if (fd < 0)
- {
- ot_util_set_error_from_errno (error, errno);
- goto out;
- }
-
- stream = (GUnixOutputStream*)g_unix_output_stream_new (fd, FALSE);
- if (!g_output_stream_write_all ((GOutputStream*)stream,
- g_variant_get_data (serialized),
- g_variant_get_size (serialized),
- &bytes_written,
- NULL,
- error))
- goto out;
- if (!g_output_stream_close ((GOutputStream*)stream,
- NULL, error))
- goto out;
-
- if (!link_one_file (self, tmp_name, OSTREE_OBJECT_TYPE_META,
- TRUE, FALSE, out_checksum, error))
- goto out;
-
- ret = TRUE;
- out:
- /* Unconditionally unlink; if we suceeded, there's a new link, if not, clean up. */
- (void) unlink (tmp_name);
- if (fd != -1)
- close (fd);
- if (serialized != NULL)
- g_variant_unref (serialized);
- g_free (tmp_name);
- g_clear_object (&stream);
- return ret;
-}
-
-gboolean
-ostree_repo_load_variant_checked (OstreeRepo *self,
- OstreeSerializedVariantType expected_type,
- const char *sha256,
- GVariant **out_variant,
- GError **error)
-{
- gboolean ret = FALSE;
- OstreeSerializedVariantType type;
- GVariant *ret_variant = NULL;
-
- if (!ostree_repo_load_variant (self, sha256, &type, &ret_variant, error))
- goto out;
-
- if (type != expected_type)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Corrupted metadata object '%s'; found type %u, expected %u", sha256,
- type, (guint32)expected_type);
- goto out;
- }
-
- ret = TRUE;
- *out_variant = ret_variant;
- ret_variant = NULL;
- out:
- if (ret_variant)
- g_variant_unref (ret_variant);
- return ret;
-}
-
-static gboolean
-import_directory_meta (OstreeRepo *self,
- const char *path,
- GVariant **out_variant,
- GChecksum **out_checksum,
- GError **error)
-{
- gboolean ret = FALSE;
- struct stat stbuf;
- GChecksum *ret_checksum = NULL;
- GVariant *dirmeta = NULL;
- GVariant *xattrs = NULL;
-
- if (lstat (path, &stbuf) < 0)
- {
- ot_util_set_error_from_errno (error, errno);
- goto out;
- }
-
- if (!S_ISDIR(stbuf.st_mode))
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Not a directory: '%s'", path);
- goto out;
- }
-
- xattrs = ostree_get_xattrs_for_path (path, error);
- if (!xattrs)
- goto out;
-
- dirmeta = g_variant_new ("(uuuu@a(ayay))",
- OSTREE_DIR_META_VERSION,
- GUINT32_TO_BE ((guint32)stbuf.st_uid),
- GUINT32_TO_BE ((guint32)stbuf.st_gid),
- GUINT32_TO_BE ((guint32)stbuf.st_mode),
- xattrs);
- g_variant_ref_sink (dirmeta);
-
- if (!import_gvariant_object (self, OSTREE_SERIALIZED_DIRMETA_VARIANT,
- dirmeta, &ret_checksum, error))
- goto out;
-
- ret = TRUE;
- out:
- if (!ret)
- {
- if (ret_checksum)
- g_checksum_free (ret_checksum);
- if (dirmeta != NULL)
- g_variant_unref (dirmeta);
- }
- else
- {
- *out_checksum = ret_checksum;
- *out_variant = dirmeta;
- }
- if (xattrs)
- g_variant_unref (xattrs);
- return ret;
-}
-
-static char *
-get_object_path (OstreeRepo *self,
- const char *checksum,
- OstreeObjectType type)
-{
- OstreeRepoPrivate *priv = GET_PRIVATE (self);
- char *ret;
- char *relpath;
-
- relpath = ostree_get_relative_object_path (checksum, type, priv->archive);
- ret = g_build_filename (priv->path, relpath, NULL);
- g_free (relpath);
-
- return ret;
-}
-
-static char *
-prepare_dir_for_checksum_get_object_path (OstreeRepo *self,
- const char *checksum,
- OstreeObjectType type,
- GError **error)
-{
- char *checksum_dir = NULL;
- char *object_path = NULL;
-
- object_path = get_object_path (self, checksum, type);
- checksum_dir = g_path_get_dirname (object_path);
-
- if (!ot_util_ensure_directory (checksum_dir, FALSE, error))
- goto out;
-
- out:
- g_free (checksum_dir);
- return object_path;
-}
-
-static gboolean
-link_object_trusted (OstreeRepo *self,
- const char *path,
- const char *checksum,
- OstreeObjectType objtype,
- gboolean ignore_exists,
- gboolean force,
- gboolean *did_exist,
- GError **error)
-{
- char *src_basename = NULL;
- char *src_dirname = NULL;
- char *dest_basename = NULL;
- char *tmp_dest_basename = NULL;
- char *dest_dirname = NULL;
- DIR *src_dir = NULL;
- DIR *dest_dir = NULL;
- gboolean ret = FALSE;
- char *dest_path = NULL;
-
- src_basename = g_path_get_basename (path);
- src_dirname = g_path_get_dirname (path);
-
- src_dir = opendir (src_dirname);
- if (src_dir == NULL)
- {
- ot_util_set_error_from_errno (error, errno);
- goto out;
- }
-
- dest_path = prepare_dir_for_checksum_get_object_path (self, checksum, objtype, error);
- if (!dest_path)
- goto out;
-
- dest_basename = g_path_get_basename (dest_path);
- dest_dirname = g_path_get_dirname (dest_path);
- dest_dir = opendir (dest_dirname);
- if (dest_dir == NULL)
- {
- ot_util_set_error_from_errno (error, errno);
- goto out;
- }
-
- if (force)
- {
- tmp_dest_basename = g_strconcat (dest_basename, ".tmp", NULL);
- (void) unlinkat (dirfd (dest_dir), tmp_dest_basename, 0);
- }
- else
- tmp_dest_basename = g_strdup (dest_basename);
-
- if (linkat (dirfd (src_dir), src_basename, dirfd (dest_dir), tmp_dest_basename, 0) < 0)
- {
- if (errno != EEXIST || !ignore_exists)
- {
- ot_util_set_error_from_errno (error, errno);
- goto out;
- }
- else
- *did_exist = TRUE;
- }
- else
- *did_exist = FALSE;
-
- if (force)
- {
- if (renameat (dirfd (dest_dir), tmp_dest_basename,
- dirfd (dest_dir), dest_basename) < 0)
- {
- ot_util_set_error_from_errno (error, errno);
- goto out;
- }
- (void) unlinkat (dirfd (dest_dir), tmp_dest_basename, 0);
- }
-
- ret = TRUE;
- out:
- if (src_dir != NULL)
- closedir (src_dir);
- if (dest_dir != NULL)
- closedir (dest_dir);
- g_free (src_basename);
- g_free (src_dirname);
- g_free (dest_basename);
- g_free (tmp_dest_basename);
- g_free (dest_dirname);
- g_free (dest_path);
- return ret;
-}
-
-static gboolean
-archive_file_trusted (OstreeRepo *self,
- const char *path,
- const char *checksum,
- OstreeObjectType objtype,
- gboolean ignore_exists,
- gboolean force,
- gboolean *did_exist,
- GError **error)
-{
- GFile *infile = NULL;
- GFile *outfile = NULL;
- GFileOutputStream *out = NULL;
- gboolean ret = FALSE;
- char *dest_path = NULL;
- char *dest_tmp_path = NULL;
-
- infile = ot_util_new_file_for_path (path);
-
- dest_path = prepare_dir_for_checksum_get_object_path (self, checksum, objtype, error);
- if (!dest_path)
- goto out;
-
- dest_tmp_path = g_strconcat (dest_path, ".tmp", NULL);
-
- outfile = ot_util_new_file_for_path (dest_tmp_path);
- out = g_file_replace (outfile, NULL, FALSE, 0, NULL, error);
- if (!out)
- goto out;
-
- if (!ostree_pack_object ((GOutputStream*)out, infile, objtype, NULL, error))
- goto out;
-
- if (!g_output_stream_close ((GOutputStream*)out, NULL, error))
- goto out;
-
- if (rename (dest_tmp_path, dest_path) < 0)
- {
- ot_util_set_error_from_errno (error, errno);
- goto out;
- }
-
- ret = TRUE;
- out:
- g_free (dest_path);
- g_free (dest_tmp_path);
- g_clear_object (&infile);
- g_clear_object (&outfile);
- g_clear_object (&out);
- return ret;
-}
-
-gboolean
-ostree_repo_store_object_trusted (OstreeRepo *self,
- const char *path,
- const char *checksum,
- OstreeObjectType objtype,
- gboolean ignore_exists,
- gboolean force,
- gboolean *did_exist,
- GError **error)
-{
- OstreeRepoPrivate *priv = GET_PRIVATE (self);
- if (priv->archive && objtype == OSTREE_OBJECT_TYPE_FILE)
- return archive_file_trusted (self, path, checksum, objtype, ignore_exists, force, did_exist, error);
- else
- return link_object_trusted (self, path, checksum, objtype, ignore_exists, force, did_exist, error);
-}
-
-static gboolean
-link_one_file (OstreeRepo *self, const char *path, OstreeObjectType type,
- gboolean ignore_exists, gboolean force,
- GChecksum **out_checksum,
- GError **error)
-{
- gboolean ret = FALSE;
- struct stat stbuf;
- GChecksum *id = NULL;
- gboolean did_exist;
-
- if (!ostree_stat_and_checksum_file (-1, path, type, &id, &stbuf, error))
- goto out;
-
- if (!ostree_repo_store_object_trusted (self, path, g_checksum_get_string (id), type,
- ignore_exists, force, &did_exist, error))
- goto out;
-
- *out_checksum = id;
- id = NULL;
- ret = TRUE;
- out:
- if (id != NULL)
- g_checksum_free (id);
- return ret;
-}
-
-gboolean
-ostree_repo_link_file (OstreeRepo *self,
- const char *path,
- gboolean ignore_exists,
- gboolean force,
- GError **error)
-{
- OstreeRepoPrivate *priv = GET_PRIVATE (self);
- GChecksum *checksum = NULL;
-
- g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
- g_return_val_if_fail (priv->inited, FALSE);
-
- if (!link_one_file (self, path, OSTREE_OBJECT_TYPE_FILE,
- ignore_exists, force, &checksum, error))
- return FALSE;
- g_checksum_free (checksum);
- return TRUE;
-}
-
-gboolean
-ostree_repo_store_packfile (OstreeRepo *self,
- const char *expected_checksum,
- const char *path,
- OstreeObjectType objtype,
- GError **error)
-{
- OstreeRepoPrivate *priv = GET_PRIVATE (self);
- gboolean ret = FALSE;
- GString *tempfile_path = NULL;
- GChecksum *checksum = NULL;
- struct stat stbuf;
- gboolean did_exist;
-
- tempfile_path = g_string_new (priv->path);
- g_string_append_printf (tempfile_path, "/tmp-unpack-%s", expected_checksum);
-
- if (!ostree_unpack_object (path, objtype, tempfile_path->str, &checksum, error))
- goto out;
-
- if (strcmp (g_checksum_get_string (checksum), expected_checksum) != 0)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Corrupted object %s (actual checksum is %s)",
- expected_checksum, g_checksum_get_string (checksum));
- goto out;
- }
-
- if (!ostree_repo_store_object_trusted (self, tempfile_path ? tempfile_path->str : path,
- expected_checksum,
- objtype,
- TRUE, FALSE, &did_exist, error))
- goto out;
-
- ret = TRUE;
- out:
- if (tempfile_path)
- {
- (void) unlink (tempfile_path->str);
- g_string_free (tempfile_path, TRUE);
- }
- if (checksum)
- g_checksum_free (checksum);
- return ret;
-}
-
-typedef struct _ParsedTreeData ParsedTreeData;
-typedef struct _ParsedDirectoryData ParsedDirectoryData;
-
-static void parsed_tree_data_free (ParsedTreeData *pdata);
-
-struct _ParsedDirectoryData {
- ParsedTreeData *tree_data;
- char *metadata_sha256;
- GVariant *meta_data;
-};
-
-static void
-parsed_directory_data_free (ParsedDirectoryData *pdata)
-{
- if (pdata == NULL)
- return;
- parsed_tree_data_free (pdata->tree_data);
- g_free (pdata->metadata_sha256);
- g_variant_unref (pdata->meta_data);
- g_free (pdata);
-}
-
-struct _ParsedTreeData {
- GHashTable *files; /* char* filename -> char* checksum */
- GHashTable *directories; /* char* dirname -> ParsedDirectoryData* */
-};
-
-static ParsedTreeData *
-parsed_tree_data_new (void)
-{
- ParsedTreeData *ret = g_new0 (ParsedTreeData, 1);
- ret->files = g_hash_table_new_full (g_str_hash, g_str_equal,
- (GDestroyNotify)g_free,
- (GDestroyNotify)g_free);
- ret->directories = g_hash_table_new_full (g_str_hash, g_str_equal,
- (GDestroyNotify)g_free,
- (GDestroyNotify)parsed_directory_data_free);
- return ret;
-}
-
-static void
-parsed_tree_data_free (ParsedTreeData *pdata)
-{
- if (pdata == NULL)
- return;
- g_hash_table_destroy (pdata->files);
- g_hash_table_destroy (pdata->directories);
- g_free (pdata);
-}
-
-static gboolean
-parse_tree (OstreeRepo *self,
- const char *sha256,
- ParsedTreeData **out_pdata,
- GError **error)
-{
- gboolean ret = FALSE;
- ParsedTreeData *ret_pdata = NULL;
- int i, n;
- guint32 version;
- GVariant *tree_variant = NULL;
- GVariant *meta_variant = NULL;
- GVariant *files_variant = NULL;
- GVariant *dirs_variant = NULL;
-
- if (!ostree_repo_load_variant_checked (self, OSTREE_SERIALIZED_TREE_VARIANT,
- sha256, &tree_variant, error))
- goto out;
-
- /* PARSE OSTREE_SERIALIZED_TREE_VARIANT */
- g_variant_get (tree_variant, "(u@a{sv}@a(ss)@a(sss))",
- &version, &meta_variant, &files_variant, &dirs_variant);
- version = GUINT32_FROM_BE (version);
-
- ret_pdata = parsed_tree_data_new ();
- n = g_variant_n_children (files_variant);
- for (i = 0; i < n; i++)
- {
- const char *filename;
- const char *checksum;
-
- g_variant_get_child (files_variant, i, "(&s&s)", &filename, &checksum);
-
- g_hash_table_insert (ret_pdata->files, g_strdup (filename), g_strdup (checksum));
- }
-
- n = g_variant_n_children (dirs_variant);
- for (i = 0; i < n; i++)
- {
- const char *dirname;
- const char *tree_checksum;
- const char *meta_checksum;
- ParsedTreeData *child_tree = NULL;
- GVariant *metadata = NULL;
- ParsedDirectoryData *child_dir = NULL;
-
- g_variant_get_child (dirs_variant, i, "(&s&s&s)",
- &dirname, &tree_checksum, &meta_checksum);
-
- if (!parse_tree (self, tree_checksum, &child_tree, error))
- goto out;
-
- if (!ostree_repo_load_variant_checked (self, OSTREE_SERIALIZED_DIRMETA_VARIANT,
- meta_checksum, &metadata, error))
- {
- parsed_tree_data_free (child_tree);
- goto out;
- }
-
- child_dir = g_new0 (ParsedDirectoryData, 1);
- child_dir->tree_data = child_tree;
- child_dir->metadata_sha256 = g_strdup (meta_checksum);
- child_dir->meta_data = metadata;
-
- g_hash_table_insert (ret_pdata->directories, g_strdup (dirname), child_dir);
- }
-
- ret = TRUE;
- out:
- if (!ret)
- parsed_tree_data_free (ret_pdata);
- else
- *out_pdata = ret_pdata;
- if (tree_variant)
- g_variant_unref (tree_variant);
- if (meta_variant)
- g_variant_unref (meta_variant);
- if (files_variant)
- g_variant_unref (files_variant);
- if (dirs_variant)
- g_variant_unref (dirs_variant);
- return ret;
-}
-
-static gboolean
-load_commit_and_trees (OstreeRepo *self,
- const char *commit_sha256,
- GVariant **out_commit,
- ParsedDirectoryData **out_root_data,
- GError **error)
-{
- GVariant *ret_commit = NULL;
- ParsedDirectoryData *ret_root_data = NULL;
- ParsedTreeData *tree_data = NULL;
- char *ret_metadata_checksum = NULL;
- GVariant *root_metadata = NULL;
- gboolean ret = FALSE;
- const char *tree_contents_checksum;
- const char *tree_meta_checksum;
-
- if (!ostree_repo_load_variant_checked (self, OSTREE_SERIALIZED_COMMIT_VARIANT,
- commit_sha256, &ret_commit, error))
- goto out;
-
- /* PARSE OSTREE_SERIALIZED_COMMIT_VARIANT */
- g_variant_get_child (ret_commit, 6, "&s", &tree_contents_checksum);
- g_variant_get_child (ret_commit, 7, "&s", &tree_meta_checksum);
-
- if (!ostree_repo_load_variant_checked (self, OSTREE_SERIALIZED_DIRMETA_VARIANT,
- tree_meta_checksum, &root_metadata, error))
- goto out;
-
- if (!parse_tree (self, tree_contents_checksum, &tree_data, error))
- goto out;
-
- ret_root_data = g_new0 (ParsedDirectoryData, 1);
- ret_root_data->tree_data = tree_data;
- ret_root_data->metadata_sha256 = g_strdup (tree_meta_checksum);
- ret_root_data->meta_data = root_metadata;
- root_metadata = NULL;
-
- ret = TRUE;
- *out_commit = ret_commit;
- ret_commit = NULL;
- *out_root_data = ret_root_data;
- ret_root_data = NULL;
- out:
- if (ret_commit)
- g_variant_unref (ret_commit);
- parsed_directory_data_free (ret_root_data);
- g_free (ret_metadata_checksum);
- if (root_metadata)
- g_variant_unref (root_metadata);
- return ret;
-}
-
-static GVariant *
-create_empty_gvariant_dict (void)
-{
- GVariantBuilder builder;
- g_variant_builder_init (&builder, G_VARIANT_TYPE("a{sv}"));
- return g_variant_builder_end (&builder);
-}
-
-static gboolean
-import_parsed_tree (OstreeRepo *self,
- ParsedTreeData *tree,
- GChecksum **out_checksum,
- GError **error)
-{
- gboolean ret = FALSE;
- GVariant *serialized_tree = NULL;
- gboolean builders_initialized = FALSE;
- GVariantBuilder files_builder;
- GVariantBuilder dirs_builder;
- GHashTableIter hash_iter;
- gpointer key, value;
-
- g_variant_builder_init (&files_builder, G_VARIANT_TYPE ("a(ss)"));
- g_variant_builder_init (&dirs_builder, G_VARIANT_TYPE ("a(sss)"));
- builders_initialized = TRUE;
-
- g_hash_table_iter_init (&hash_iter, tree->files);
- while (g_hash_table_iter_next (&hash_iter, &key, &value))
- {
- const char *name = key;
- const char *checksum = value;
-
- g_variant_builder_add (&files_builder, "(ss)", name, checksum);
- }
-
- g_hash_table_iter_init (&hash_iter, tree->directories);
- while (g_hash_table_iter_next (&hash_iter, &key, &value))
- {
- const char *name = key;
- GChecksum *dir_checksum = NULL;
- ParsedDirectoryData *dir = value;
-
- if (!import_parsed_tree (self, dir->tree_data, &dir_checksum, error))
- goto out;
-
- g_variant_builder_add (&dirs_builder, "(sss)",
- name, g_checksum_get_string (dir_checksum), dir->metadata_sha256);
- g_checksum_free (dir_checksum);
- }
-
- serialized_tree = g_variant_new ("(u@a{sv}@a(ss)@a(sss))",
- GUINT32_TO_BE (0),
- create_empty_gvariant_dict (),
- g_variant_builder_end (&files_builder),
- g_variant_builder_end (&dirs_builder));
- builders_initialized = FALSE;
- g_variant_ref_sink (serialized_tree);
- if (!import_gvariant_object (self, OSTREE_SERIALIZED_TREE_VARIANT, serialized_tree, out_checksum, error))
- goto out;
-
- ret = TRUE;
- out:
- if (builders_initialized)
- {
- g_variant_builder_clear (&files_builder);
- g_variant_builder_clear (&dirs_builder);
- }
- if (serialized_tree)
- g_variant_unref (serialized_tree);
- return ret;
-}
-
-static gboolean
-check_path (const char *filename,
- GError **error)
-{
- gboolean ret = FALSE;
-
- if (!*filename)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Invalid empty filename");
- goto out;
- }
-
- if (strcmp (filename, ".") == 0)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Self-reference '.' in filename '%s' not allowed (yet)", filename);
- goto out;
- }
-
- if (ot_util_filename_has_dotdot (filename))
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Path uplink '..' in filename '%s' not allowed (yet)", filename);
- goto out;
- }
-
- if (g_path_is_absolute (filename))
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Absolute filename '%s' not allowed (yet)", filename);
- goto out;
- }
-
- ret = TRUE;
- out:
- return ret;
-}
-
-static gboolean
-walk_parsed_tree (OstreeRepo *self,
- const char *filename,
- ParsedTreeData *tree,
- int *out_filename_index, /* out*/
- char **out_component, /* out, must free */
- ParsedTreeData **out_tree, /* out, but do not free */
- GError **error)
-{
- gboolean ret = FALSE;
- GPtrArray *components = NULL;
- ParsedTreeData *current_tree = tree;
- const char *component = NULL;
- const char *file_sha1 = NULL;
- ParsedDirectoryData *dir = NULL;
- int i;
- int ret_filename_index = 0;
-
- components = ot_util_path_split (filename);
- g_assert (components != NULL);
-
- current_tree = tree;
- for (i = 0; i < components->len - 1; i++)
- {
- component = components->pdata[i];
- file_sha1 = g_hash_table_lookup (current_tree->files, component);
- dir = g_hash_table_lookup (current_tree->directories, component);
-
- if (!(file_sha1 || dir))
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "No such file or directory: %s",
- filename);
- goto out;
- }
- else if (file_sha1)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Encountered non-directory '%s' in '%s'",
- (char*)component,
- filename);
- goto out;
- }
- else
- {
- g_assert (dir != NULL);
- current_tree = dir->tree_data;
- ret_filename_index++;
- }
- }
-
- ret = TRUE;
- *out_filename_index = i;
- *out_component = components->pdata[components->len-1];
- components->pdata[components->len-1] = NULL; /* steal */
- *out_tree = current_tree;
- out:
- g_ptr_array_free (components, TRUE);
- return ret;
-}
-
-static gboolean
-remove_files_from_tree (OstreeRepo *self,
- const char *base,
- GPtrArray *removed_files,
- ParsedTreeData *tree,
- GError **error)
-{
- gboolean ret = FALSE;
- int i;
-
- for (i = 0; i < removed_files->len; i++)
- {
- const char *filename = removed_files->pdata[i];
- int filename_index;
- char *component = NULL;
- ParsedTreeData *parent;
- const char *file_sha1;
- ParsedTreeData *dir;
-
- if (!check_path (filename, error))
- goto out;
-
- if (!walk_parsed_tree (self, filename, tree,
- &filename_index, (char**)&component, &parent,
- error))
- goto out;
-
- file_sha1 = g_hash_table_lookup (parent->files, component);
- dir = g_hash_table_lookup (parent->directories, component);
-
- if (file_sha1)
- g_hash_table_remove (parent->files, component);
- else if (dir)
- g_hash_table_remove (parent->directories, component);
- else
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "No such file or directory: %s",
- filename);
- g_free (component);
- goto out;
- }
- g_free (component);
- }
-
- ret = TRUE;
- out:
- return ret;
-}
-
-static gboolean
-add_one_directory_to_tree_and_import (OstreeRepo *self,
- const char *basename,
- const char *abspath,
- ParsedTreeData *tree,
- ParsedDirectoryData **dir, /*inout*/
- GError **error)
-{
- gboolean ret = FALSE;
- GVariant *dirmeta = NULL;
- GChecksum *dir_meta_checksum = NULL;
- ParsedDirectoryData *dir_value = *dir;
-
- g_assert (tree != NULL);
-
- if (!import_directory_meta (self, abspath, &dirmeta, &dir_meta_checksum, error))
- goto out;
-
- if (dir_value)
- {
- g_variant_unref (dir_value->meta_data);
- dir_value->meta_data = dirmeta;
- }
- else
- {
- dir_value = g_new0 (ParsedDirectoryData, 1);
- dir_value->tree_data = parsed_tree_data_new ();
- dir_value->metadata_sha256 = g_strdup (g_checksum_get_string (dir_meta_checksum));
- dir_value->meta_data = dirmeta;
- g_hash_table_insert (tree->directories, g_strdup (basename), dir_value);
- }
-
- ret = TRUE;
- *dir = dir_value;
- out:
- if (dir_meta_checksum)
- g_checksum_free (dir_meta_checksum);
- return ret;
-}
-
-static gboolean
-add_one_file_to_tree_and_import (OstreeRepo *self,
- const char *basename,
- const char *abspath,
- ParsedTreeData *tree,
- GError **error)
-{
- gboolean ret = FALSE;
- GChecksum *checksum = NULL;
-
- g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
- g_assert (tree != NULL);
-
- if (!link_one_file (self, abspath, OSTREE_OBJECT_TYPE_FILE,
- TRUE, FALSE, &checksum, error))
- goto out;
-
- g_hash_table_replace (tree->files, g_strdup (basename),
- g_strdup (g_checksum_get_string (checksum)));
-
- ret = TRUE;
- out:
- if (checksum)
- g_checksum_free (checksum);
- return ret;
-}
-
-static gboolean
-add_one_path_to_tree_and_import (OstreeRepo *self,
- const char *base,
- const char *filename,
- ParsedTreeData *tree,
- GError **error)
-{
- gboolean ret = FALSE;
- GPtrArray *components = NULL;
- struct stat stbuf;
- char *component_abspath = NULL;
- ParsedTreeData *current_tree = tree;
- const char *component = NULL;
- const char *file_sha1;
- char *abspath = NULL;
- ParsedDirectoryData *dir;
- int i;
- gboolean is_directory;
-
- if (!check_path (filename, error))
- goto out;
-
- abspath = g_build_filename (base, filename, NULL);
-
- if (lstat (abspath, &stbuf) < 0)
- {
- ot_util_set_error_from_errno (error, errno);
- goto out;
- }
- is_directory = S_ISDIR(stbuf.st_mode);
-
- if (components)
- g_ptr_array_free (components, TRUE);
- components = ot_util_path_split (filename);
- g_assert (components->len > 0);
-
- current_tree = tree;
- for (i = 0; i < components->len; i++)
- {
- component = components->pdata[i];
- g_free (component_abspath);
- component_abspath = ot_util_path_join_n (base, components, i);
- file_sha1 = g_hash_table_lookup (current_tree->files, component);
- dir = g_hash_table_lookup (current_tree->directories, component);
-
- g_assert_cmpstr (component, !=, ".");
-
- if (i < components->len - 1)
- {
- if (file_sha1 != NULL)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Encountered non-directory '%s' in '%s'",
- component,
- filename);
- goto out;
- }
- /* Implicitly add intermediate directories */
- if (!add_one_directory_to_tree_and_import (self, component,
- component_abspath, current_tree, &dir,
- error))
- goto out;
- g_assert (dir != NULL);
- current_tree = dir->tree_data;
- }
- else if (is_directory)
- {
- if (file_sha1 != NULL)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "File '%s' can't be overwritten by directory",
- filename);
- goto out;
- }
- if (!add_one_directory_to_tree_and_import (self, component,
- abspath, current_tree, &dir,
- error))
- goto out;
- }
- else
- {
- g_assert (!is_directory);
- if (dir != NULL)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "File '%s' can't be overwritten by directory",
- filename);
- goto out;
- }
- if (!add_one_file_to_tree_and_import (self, component, abspath,
- current_tree, error))
- goto out;
- }
- }
-
- ret = TRUE;
- out:
- if (components)
- g_ptr_array_unref (components);
- g_free (component_abspath);
- g_free (abspath);
- return ret;
-}
-
-static gboolean
-add_files_to_tree_and_import (OstreeRepo *self,
- const char *base,
- GPtrArray *added_files,
- ParsedTreeData *tree,
- GError **error)
-{
- gboolean ret = FALSE;
- int i;
-
- for (i = 0; i < added_files->len; i++)
- {
- const char *path = added_files->pdata[i];
-
- if (!add_one_path_to_tree_and_import (self, base, path, tree, error))
- goto out;
- }
-
- ret = TRUE;
- out:
- return ret;
-}
-
-gboolean
-ostree_repo_write_ref (OstreeRepo *self,
- gboolean is_local,
- const char *name,
- const char *rev,
- GError **error)
-{
- OstreeRepoPrivate *priv = GET_PRIVATE (self);
- return write_checksum_file (is_local ? priv->local_heads_dir : priv->remote_heads_dir,
- name, rev, error);
-}
-
-static gboolean
-commit_parsed_tree (OstreeRepo *self,
- const char *branch,
- const char *parent,
- const char *subject,
- const char *body,
- GVariant *metadata,
- ParsedDirectoryData *root,
- GChecksum **out_commit,
- GError **error)
-{
- gboolean ret = FALSE;
- GChecksum *root_checksum = NULL;
- GChecksum *ret_commit = NULL;
- GVariant *commit = NULL;
- GDateTime *now = NULL;
-
- g_assert (branch != NULL);
- g_assert (subject != NULL);
-
- if (!import_parsed_tree (self, root->tree_data, &root_checksum, error))
- goto out;
-
- now = g_date_time_new_now_utc ();
- commit = g_variant_new ("(u@a{sv}ssstss)",
- GUINT32_TO_BE (OSTREE_COMMIT_VERSION),
- create_empty_gvariant_dict (),
- parent ? parent : "",
- subject, body ? body : "",
- GUINT64_TO_BE (g_date_time_to_unix (now)),
- g_checksum_get_string (root_checksum),
- root->metadata_sha256);
- g_variant_ref_sink (commit);
- if (!import_gvariant_object (self, OSTREE_SERIALIZED_COMMIT_VARIANT,
- commit, &ret_commit, error))
- goto out;
-
- if (!ostree_repo_write_ref (self, TRUE, branch, g_checksum_get_string (ret_commit), error))
- goto out;
-
- ret = TRUE;
- *out_commit = ret_commit;
- out:
- if (root_checksum)
- g_checksum_free (root_checksum);
- if (commit)
- g_variant_unref (commit);
- if (now)
- g_date_time_unref (now);
- return ret;
-}
-
-static gboolean
-import_root (OstreeRepo *self,
- const char *base,
- ParsedDirectoryData **out_root,
- GError **error)
-{
- gboolean ret = FALSE;
- ParsedDirectoryData *ret_root = NULL;
- GVariant *root_metadata = NULL;
- GChecksum *root_meta_checksum = NULL;
-
- if (!import_directory_meta (self, base, &root_metadata, &root_meta_checksum, error))
- goto out;
-
- ret_root = g_new0 (ParsedDirectoryData, 1);
- ret_root->tree_data = parsed_tree_data_new ();
- ret_root->meta_data = root_metadata;
- root_metadata = NULL;
- ret_root->metadata_sha256 = g_strdup (g_checksum_get_string (root_meta_checksum));
-
- ret = TRUE;
- *out_root = ret_root;
- ret_root = NULL;
- out:
- if (root_metadata)
- g_variant_unref (root_metadata);
- if (root_meta_checksum)
- g_checksum_free (root_meta_checksum);
- parsed_directory_data_free (ret_root);
- return ret;
-}
-
-gboolean
-ostree_repo_commit (OstreeRepo *self,
- const char *branch,
- const char *parent,
- const char *subject,
- const char *body,
- GVariant *metadata,
- const char *base,
- GPtrArray *modified_files,
- GPtrArray *removed_files,
- GChecksum **out_commit,
- GError **error)
-{
- OstreeRepoPrivate *priv = GET_PRIVATE (self);
- gboolean ret = FALSE;
- ParsedDirectoryData *root = NULL;
- GVariant *previous_commit = NULL;
- GChecksum *ret_commit_checksum = NULL;
- GVariant *root_metadata = NULL;
- GChecksum *root_meta_checksum = NULL;
- char *current_head = NULL;
-
- g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
- g_return_val_if_fail (priv->inited, FALSE);
- g_return_val_if_fail (branch != NULL, FALSE);
- g_return_val_if_fail (subject != NULL, FALSE);
-
- if (parent == NULL)
- parent = branch;
-
- if (!resolve_rev (self, parent, TRUE, ¤t_head, error))
- goto out;
-
- if (current_head)
- {
- if (!load_commit_and_trees (self, current_head, &previous_commit, &root, error))
- goto out;
- if (!import_directory_meta (self, base, &root_metadata, &root_meta_checksum, error))
- goto out;
- g_variant_unref (root->meta_data);
- root->meta_data = root_metadata;
- root_metadata = NULL;
- root->metadata_sha256 = g_strdup (g_checksum_get_string (root_meta_checksum));
- }
- else
- {
- /* Initial commit */
- if (!import_root (self, base, &root, error))
- goto out;
- }
-
- if (!remove_files_from_tree (self, base, removed_files, root->tree_data, error))
- goto out;
-
- if (!add_files_to_tree_and_import (self, base, modified_files, root->tree_data, error))
- goto out;
-
- if (!commit_parsed_tree (self, branch, current_head,
- subject, body, metadata, root,
- &ret_commit_checksum, error))
- goto out;
-
- ret = TRUE;
- *out_commit = ret_commit_checksum;
- ret_commit_checksum = NULL;
- out:
- if (ret_commit_checksum)
- g_checksum_free (ret_commit_checksum);
- g_free (current_head);
- if (previous_commit)
- g_variant_unref (previous_commit);
- parsed_directory_data_free (root);
- if (root_metadata)
- g_variant_unref (root_metadata);
- if (root_meta_checksum)
- g_checksum_free (root_meta_checksum);
- return ret;
-}
-
-gboolean
-ostree_repo_commit_from_filelist_fd (OstreeRepo *self,
- const char *branch,
- const char *parent,
- const char *subject,
- const char *body,
- GVariant *metadata,
- const char *base,
- int fd,
- char separator,
- GChecksum **out_commit,
- GError **error)
-{
- OstreeRepoPrivate *priv = GET_PRIVATE (self);
- gboolean ret = FALSE;
- ParsedDirectoryData *root = NULL;
- GChecksum *ret_commit_checksum = NULL;
- GUnixInputStream *in = NULL;
- GDataInputStream *datain = NULL;
- char *filename = NULL;
- gsize filename_len;
- GError *temp_error = NULL;
- GVariant *root_metadata = NULL;
- GChecksum *root_meta_checksum = NULL;
- char *current_head = NULL;
-
- g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
- g_return_val_if_fail (priv->inited, FALSE);
- g_return_val_if_fail (branch != NULL, FALSE);
- g_return_val_if_fail (subject != NULL, FALSE);
-
- if (parent == NULL)
- parent = branch;
-
- /* We're overwriting the tree */
- if (!import_root (self, base, &root, error))
- goto out;
-
- if (!resolve_rev (self, parent, TRUE, ¤t_head, error))
- goto out;
-
- in = (GUnixInputStream*)g_unix_input_stream_new (fd, FALSE);
- datain = g_data_input_stream_new ((GInputStream*)in);
-
- while ((filename = g_data_input_stream_read_upto (datain, &separator, 1,
- &filename_len, NULL, &temp_error)) != NULL)
- {
- if (!g_data_input_stream_read_byte (datain, NULL, &temp_error))
- {
- if (temp_error != NULL)
- {
- g_propagate_prefixed_error (error, temp_error, "%s", "While reading filelist: ");
- goto out;
- }
- }
- if (!add_one_path_to_tree_and_import (self, base, filename, root->tree_data, error))
- goto out;
- g_free (filename);
- filename = NULL;
- }
- if (filename == NULL && temp_error != NULL)
- {
- g_propagate_prefixed_error (error, temp_error, "%s", "While reading filelist: ");
- goto out;
- }
- if (!commit_parsed_tree (self, branch, current_head, subject, body, metadata,
- root, &ret_commit_checksum, error))
- goto out;
-
- ret = TRUE;
- *out_commit = ret_commit_checksum;
- ret_commit_checksum = NULL;
- out:
- if (ret_commit_checksum)
- g_checksum_free (ret_commit_checksum);
- g_free (current_head);
- if (root_metadata)
- g_variant_unref (root_metadata);
- if (root_meta_checksum)
- g_checksum_free (root_meta_checksum);
- g_clear_object (&datain);
- g_clear_object (&in);
- g_free (filename);
- parsed_directory_data_free (root);
- return ret;
-
-}
-
-static gboolean
-iter_object_dir (OstreeRepo *self,
- GFile *dir,
- OstreeRepoObjectIter callback,
- gpointer user_data,
- GError **error)
-{
- gboolean ret = FALSE;
- GError *temp_error = NULL;
- GFileEnumerator *enumerator = NULL;
- GFileInfo *file_info = NULL;
- char *dirpath = NULL;
-
- dirpath = g_file_get_path (dir);
-
- enumerator = g_file_enumerate_children (dir, "standard::name,standard::type,unix::*",
- G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
- NULL,
- error);
- if (!enumerator)
- goto out;
-
- while ((file_info = g_file_enumerator_next_file (enumerator, NULL, &temp_error)) != NULL)
- {
- const char *name;
- guint32 type;
- name = g_file_info_get_attribute_byte_string (file_info, "standard::name");
- type = g_file_info_get_attribute_uint32 (file_info, "standard::type");
-
- if (type != G_FILE_TYPE_DIRECTORY
- && (g_str_has_suffix (name, ".meta")
- || g_str_has_suffix (name, ".file")
- || g_str_has_suffix (name, ".packfile")))
- {
- char *dot;
- char *path;
-
- dot = strrchr (name, '.');
- g_assert (dot);
-
- if ((dot - name) == 62)
- {
- path = g_build_filename (dirpath, name, NULL);
- callback (self, path, file_info, user_data);
- g_free (path);
- }
- }
-
- g_object_unref (file_info);
- }
- if (file_info == NULL && temp_error != NULL)
- {
- g_propagate_error (error, temp_error);
- goto out;
- }
- if (!g_file_enumerator_close (enumerator, NULL, error))
- goto out;
-
- ret = TRUE;
- out:
- g_free (dirpath);
- return ret;
-}
-
-gboolean
-ostree_repo_iter_objects (OstreeRepo *self,
- OstreeRepoObjectIter callback,
- gpointer user_data,
- GError **error)
-{
- OstreeRepoPrivate *priv = GET_PRIVATE (self);
- GFile *objectdir = NULL;
- GFileEnumerator *enumerator = NULL;
- gboolean ret = FALSE;
- GFileInfo *file_info = NULL;
- GError *temp_error = NULL;
-
- g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
- g_return_val_if_fail (priv->inited, FALSE);
-
- objectdir = ot_util_new_file_for_path (priv->objects_path);
- enumerator = g_file_enumerate_children (objectdir, "standard::name,standard::type,unix::*",
- G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
- NULL,
- error);
- if (!enumerator)
- goto out;
-
- while ((file_info = g_file_enumerator_next_file (enumerator, NULL, &temp_error)) != NULL)
- {
- const char *name;
- guint32 type;
-
- name = g_file_info_get_attribute_byte_string (file_info, "standard::name");
- type = g_file_info_get_attribute_uint32 (file_info, "standard::type");
-
- if (strlen (name) == 2 && type == G_FILE_TYPE_DIRECTORY)
- {
- GFile *objdir = g_file_get_child (objectdir, name);
- if (!iter_object_dir (self, objdir, callback, user_data, error))
- {
- g_object_unref (objdir);
- goto out;
- }
- g_object_unref (objdir);
- }
- g_object_unref (file_info);
- }
- if (file_info == NULL && temp_error != NULL)
- {
- g_propagate_error (error, temp_error);
- goto out;
- }
- if (!g_file_enumerator_close (enumerator, NULL, error))
- goto out;
-
- ret = TRUE;
- out:
- g_clear_object (&file_info);
- g_clear_object (&enumerator);
- g_clear_object (&objectdir);
- return ret;
-}
-
-gboolean
-ostree_repo_load_variant (OstreeRepo *self,
- const char *sha256,
- OstreeSerializedVariantType *out_type,
- GVariant **out_variant,
- GError **error)
-{
- gboolean ret = FALSE;
- OstreeSerializedVariantType ret_type;
- GVariant *ret_variant = NULL;
- char *path = NULL;
-
- g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
-
- path = get_object_path (self, sha256, OSTREE_OBJECT_TYPE_META);
- if (!ostree_parse_metadata_file (path, &ret_type, &ret_variant, error))
- goto out;
-
- ret = TRUE;
- *out_type = ret_type;
- *out_variant = ret_variant;
- ret_variant = NULL;
- out:
- if (ret_variant)
- g_variant_unref (ret_variant);
- g_free (path);
- return ret;
-}
-
-static gboolean
-checkout_tree (OstreeRepo *self,
- ParsedTreeData *tree,
- const char *destination,
- GError **error);
-
-static gboolean
-checkout_one_directory (OstreeRepo *self,
- const char *destination,
- const char *dirname,
- ParsedDirectoryData *dir,
- GError **error)
-{
- gboolean ret = FALSE;
- char *dest_path = NULL;
- guint32 version, uid, gid, mode;
- GVariant *xattr_variant = NULL;
-
- dest_path = g_build_filename (destination, dirname, NULL);
-
- /* PARSE OSTREE_SERIALIZED_DIRMETA_VARIANT */
- g_variant_get (dir->meta_data, "(uuuu@a(ayay))",
- &version, &uid, &gid, &mode,
- &xattr_variant);
- version = GUINT32_FROM_BE (version);
- uid = GUINT32_FROM_BE (uid);
- gid = GUINT32_FROM_BE (gid);
- mode = GUINT32_FROM_BE (mode);
-
- if (mkdir (dest_path, (mode_t)mode) < 0)
- {
- ot_util_set_error_from_errno (error, errno);
- g_prefix_error (error, "Failed to create directory '%s': ", dest_path);
- goto out;
- }
-
- if (!checkout_tree (self, dir->tree_data, dest_path, error))
- goto out;
-
- if (!ostree_set_xattrs (dest_path, xattr_variant, error))
- goto out;
-
- ret = TRUE;
- out:
- g_free (dest_path);
- g_variant_unref (xattr_variant);
- return ret;
-}
-
-static gboolean
-checkout_tree (OstreeRepo *self,
- ParsedTreeData *tree,
- const char *destination,
- GError **error)
-{
- OstreeRepoPrivate *priv = GET_PRIVATE (self);
- gboolean ret = FALSE;
- GHashTableIter hash_iter;
- gpointer key, value;
-
- g_hash_table_iter_init (&hash_iter, tree->files);
- while (g_hash_table_iter_next (&hash_iter, &key, &value))
- {
- const char *filename = key;
- const char *checksum = value;
- char *object_path;
- char *dest_path;
-
- object_path = get_object_path (self, checksum, OSTREE_OBJECT_TYPE_FILE);
- dest_path = g_build_filename (destination, filename, NULL);
-
- if (priv->archive)
- {
- if (!ostree_unpack_object (object_path, OSTREE_OBJECT_TYPE_FILE, dest_path, NULL, error))
- goto out;
- }
- else
- {
- if (link (object_path, dest_path) < 0)
- {
- ot_util_set_error_from_errno (error, errno);
- g_free (object_path);
- g_free (dest_path);
- goto out;
- }
- g_free (object_path);
- g_free (dest_path);
- }
- }
-
- g_hash_table_iter_init (&hash_iter, tree->directories);
- while (g_hash_table_iter_next (&hash_iter, &key, &value))
- {
- const char *dirname = key;
- ParsedDirectoryData *dir = value;
-
- if (!checkout_one_directory (self, destination, dirname, dir, error))
- goto out;
- }
-
- ret = TRUE;
- out:
- return ret;
-}
-
-gboolean
-ostree_repo_checkout (OstreeRepo *self,
- const char *rev,
- const char *destination,
- GError **error)
-{
- gboolean ret = FALSE;
- GVariant *commit = NULL;
- char *resolved = NULL;
- char *root_meta_sha = NULL;
- ParsedDirectoryData *root = NULL;
-
- if (g_file_test (destination, G_FILE_TEST_EXISTS))
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Destination path '%s' already exists",
- destination);
- goto out;
- }
-
- if (!resolve_rev (self, rev, FALSE, &resolved, error))
- goto out;
-
- if (!load_commit_and_trees (self, resolved, &commit, &root, error))
- goto out;
-
- if (!checkout_one_directory (self, destination, NULL, root, error))
- goto out;
-
- ret = TRUE;
- out:
- g_free (resolved);
- if (commit)
- g_variant_unref (commit);
- parsed_directory_data_free (root);
- g_free (root_meta_sha);
- return ret;
-}
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-/* ostree-repo.h */
-
-#ifndef _OSTREE_REPO
-#define _OSTREE_REPO
-
-#include <glib-object.h>
-
-G_BEGIN_DECLS
-
-#define OSTREE_TYPE_REPO ostree_repo_get_type()
-#define OSTREE_REPO(obj) \
- (G_TYPE_CHECK_INSTANCE_CAST ((obj), OSTREE_TYPE_REPO, OstreeRepo))
-#define OSTREE_REPO_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_CAST ((klass), OSTREE_TYPE_REPO, OstreeRepoClass))
-#define OSTREE_IS_REPO(obj) \
- (G_TYPE_CHECK_INSTANCE_TYPE ((obj), OSTREE_TYPE_REPO))
-#define OSTREE_IS_REPO_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_TYPE ((klass), OSTREE_TYPE_REPO))
-#define OSTREE_REPO_GET_CLASS(obj) \
- (G_TYPE_INSTANCE_GET_CLASS ((obj), OSTREE_TYPE_REPO, OstreeRepoClass))
-
-typedef struct {
- GObject parent;
-} OstreeRepo;
-
-typedef struct {
- GObjectClass parent_class;
-} OstreeRepoClass;
-
-GType ostree_repo_get_type (void);
-
-OstreeRepo* ostree_repo_new (const char *path);
-
-gboolean ostree_repo_check (OstreeRepo *self, GError **error);
-
-const char * ostree_repo_get_path (OstreeRepo *self);
-
-gboolean ostree_repo_is_archive (OstreeRepo *self);
-
-GKeyFile * ostree_repo_get_config (OstreeRepo *self);
-
-GKeyFile * ostree_repo_copy_config (OstreeRepo *self);
-
-gboolean ostree_repo_write_config (OstreeRepo *self,
- GKeyFile *new_config,
- GError **error);
-
-gboolean ostree_repo_link_file (OstreeRepo *self,
- const char *path,
- gboolean ignore_exists,
- gboolean force,
- GError **error);
-
-gboolean ostree_repo_store_packfile (OstreeRepo *self,
- const char *expected_checksum,
- const char *path,
- OstreeObjectType objtype,
- GError **error);
-
-gboolean ostree_repo_store_object_trusted (OstreeRepo *self,
- const char *path,
- const char *checksum,
- OstreeObjectType objtype,
- gboolean ignore_exists,
- gboolean force,
- gboolean *did_exist,
- GError **error);
-
-gboolean ostree_repo_resolve_rev (OstreeRepo *self,
- const char *rev,
- char **out_resolved,
- GError **error);
-
-gboolean ostree_repo_write_ref (OstreeRepo *self,
- gboolean is_local,
- const char *name,
- const char *rev,
- GError **error);
-
-gboolean ostree_repo_load_variant (OstreeRepo *self,
- const char *sha256,
- OstreeSerializedVariantType *out_type,
- GVariant **out_variant,
- GError **error);
-
-gboolean ostree_repo_load_variant_checked (OstreeRepo *self,
- OstreeSerializedVariantType expected_type,
- const char *sha256,
- GVariant **out_variant,
- GError **error);
-
-gboolean ostree_repo_commit (OstreeRepo *self,
- const char *branch,
- const char *parent,
- const char *subject,
- const char *body,
- GVariant *metadata,
- const char *base,
- GPtrArray *modified_files,
- GPtrArray *removed_files,
- GChecksum **out_commit,
- GError **error);
-
-gboolean ostree_repo_commit_from_filelist_fd (OstreeRepo *self,
- const char *branch,
- const char *parent,
- const char *subject,
- const char *body,
- GVariant *metadata,
- const char *base,
- int fd,
- char separator,
- GChecksum **out_commit,
- GError **error);
-
-gboolean ostree_repo_checkout (OstreeRepo *self,
- const char *ref,
- const char *destination,
- GError **error);
-
-typedef void (*OstreeRepoObjectIter) (OstreeRepo *self, const char *path,
- GFileInfo *fileinfo, gpointer user_data);
-
-gboolean ostree_repo_iter_objects (OstreeRepo *self,
- OstreeRepoObjectIter callback,
- gpointer user_data,
- GError **error);
-
-G_END_DECLS
-
-#endif /* _OSTREE_REPO */
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#ifndef __OSTREE_H__
-
-#include <ostree-core.h>
-#include <ostree-repo.h>
-#include <ostree-checkout.h>
-
-#endif
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include <gio/gio.h>
-#include <gio/gunixinputstream.h>
-
-#include <string.h>
-
-#include "otutil.h"
-
-gboolean
-ot_util_ensure_directory (const char *path, gboolean with_parents, GError **error)
-{
- GFile *dir;
- GError *temp_error = NULL;
- gboolean ret = FALSE;
-
- dir = g_file_new_for_path (path);
- if (with_parents)
- ret = g_file_make_directory_with_parents (dir, NULL, &temp_error);
- else
- ret = g_file_make_directory (dir, NULL, &temp_error);
- if (!ret)
- {
- if (!g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_EXISTS))
- {
- g_propagate_error (error, temp_error);
- goto out;
- }
- else
- g_clear_error (&temp_error);
- }
-
- ret = TRUE;
- out:
- g_clear_object (&dir);
- return ret;
-}
-
-
-char *
-ot_util_get_file_contents_utf8 (const char *path,
- GError **error)
-{
- GFile *file = NULL;
- char *ret = NULL;
-
- file = ot_util_new_file_for_path (path);
- if (!ot_util_gfile_load_contents_utf8 (file, NULL, &ret, NULL, error))
- goto out;
-
- out:
- g_clear_object (&file);
- return ret;
-}
-
-gboolean
-ot_util_gfile_load_contents_utf8 (GFile *file,
- GCancellable *cancellable,
- char **contents_out,
- char **etag_out,
- GError **error)
-{
- char *ret_contents = NULL;
- char *ret_etag = NULL;
- gsize len;
- gboolean ret = FALSE;
-
- if (!g_file_load_contents (file, cancellable, &ret_contents, &len, &ret_etag, error))
- goto out;
- if (!g_utf8_validate (ret_contents, len, NULL))
- {
- g_set_error (error,
- G_IO_ERROR,
- G_IO_ERROR_INVALID_DATA,
- "Invalid UTF-8");
- goto out;
- }
-
- if (contents_out)
- *contents_out = ret_contents;
- else
- g_free (ret_contents);
- ret_contents = NULL;
- if (etag_out)
- *etag_out = ret_etag;
- else
- g_free (ret_etag);
- ret_etag = NULL;
- ret = TRUE;
- out:
- g_free (ret_contents);
- g_free (ret_etag);
- return ret;
-}
-
-GInputStream *
-ot_util_read_file_noatime (GFile *file, GCancellable *cancellable, GError **error)
-{
- GInputStream *ret = NULL;
- int fd;
- int flags = O_RDONLY;
- char *path = NULL;
-
- path = g_file_get_path (file);
-#ifdef O_NOATIME
- flags |= O_NOATIME;
-#endif
- fd = open (path, flags);
- if (fd < 0)
- {
- ot_util_set_error_from_errno (error, errno);
- goto out;
- }
-
- ret = (GInputStream*)g_unix_input_stream_new (fd, TRUE);
-
- out:
- g_free (path);
- return ret;
-}
-
-/* Like g_file_new_for_path, but only do local stuff, not GVFS */
-GFile *
-ot_util_new_file_for_path (const char *path)
-{
- return g_vfs_get_file_for_path (g_vfs_get_local (), path);
-}
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#ifndef __OSTREE_GIO_UTILS_H__
-#define __OSTREE_GIO_UTILS_H__
-
-#include <gio/gio.h>
-
-G_BEGIN_DECLS
-
-GFile *ot_util_new_file_for_path (const char *path);
-
-gboolean ot_util_ensure_directory (const char *path, gboolean with_parents, GError **error);
-
-char * ot_util_get_file_contents_utf8 (const char *path, GError **error);
-
-gboolean ot_util_gfile_load_contents_utf8 (GFile *file,
- GCancellable *cancellable,
- char **contents_out,
- char **etag_out,
- GError **error);
-
-GInputStream *ot_util_read_file_noatime (GFile *file, GCancellable *cancellable, GError **error);
-
-G_END_DECLS
-
-#endif
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include <gio/gio.h>
-
-#include <string.h>
-
-#include "otutil.h"
-
-void
-ot_util_usage_error (GOptionContext *context, const char *message, GError **error)
-{
- gchar *help = g_option_context_get_help (context, TRUE, NULL);
- g_printerr ("%s\n", help);
- g_free (help);
- g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, message);
-}
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#ifndef __OSTREE_GIO_UTILS_H__
-#define __OSTREE_GIO_UTILS_H__
-
-#include <gio/gio.h>
-
-G_BEGIN_DECLS
-
-void ot_util_usage_error (GOptionContext *context, const char *message, GError **error);
-
-G_END_DECLS
-
-#endif
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-unix-utils.h"
-
-#include <gio/gio.h>
-#include <gio/gunixoutputstream.h>
-
-#include <string.h>
-#include <sys/types.h>
-#include <dirent.h>
-
-gboolean
-ot_util_spawn_pager (GOutputStream **out_stream,
- GError **error)
-{
- const char *pager;
- char *argv[2];
- int stdin_fd;
- pid_t pid;
- gboolean ret = FALSE;
- GOutputStream *ret_stream = NULL;
-
- if (!isatty (1))
- {
- ret_stream = (GOutputStream*)g_unix_output_stream_new (1, TRUE);
- }
- else
- {
- pager = g_getenv ("GIT_PAGER");
- if (pager == NULL)
- pager = "less";
-
- argv[0] = (char*)pager;
- argv[1] = NULL;
-
- if (!g_spawn_async_with_pipes (NULL, argv, NULL, G_SPAWN_SEARCH_PATH,
- NULL, NULL, &pid, &stdin_fd, NULL, NULL, error))
- {
- g_prefix_error (error, "%s", "Failed to spawn pager: ");
- goto out;
- }
-
- ret_stream = (GOutputStream*)g_unix_output_stream_new (stdin_fd, TRUE);
- }
-
- *out_stream = ret_stream;
- ret_stream = NULL;
- ret = TRUE;
- out:
- g_clear_object (&ret_stream);
- return ret;
-}
-
-static int
-compare_filenames_by_component_length (const char *a,
- const char *b)
-{
- char *a_slash, *b_slash;
-
- a_slash = strchr (a, '/');
- b_slash = strchr (b, '/');
- while (a_slash && b_slash)
- {
- a = a_slash + 1;
- b = b_slash + 1;
- a_slash = strchr (a, '/');
- b_slash = strchr (b, '/');
- }
- if (a_slash)
- return -1;
- else if (b_slash)
- return 1;
- else
- return 0;
-}
-
-GPtrArray *
-ot_util_sort_filenames_by_component_length (GPtrArray *files)
-{
- GPtrArray *array = g_ptr_array_sized_new (files->len);
- memcpy (array->pdata, files->pdata, sizeof (gpointer) * files->len);
- g_ptr_array_sort (array, (GCompareFunc) compare_filenames_by_component_length);
- return array;
-}
-
-int
-ot_util_count_filename_components (const char *path)
-{
- int i = 0;
-
- while (path)
- {
- i++;
- path = strchr (path, '/');
- if (path)
- path++;
- }
- return i;
-}
-
-gboolean
-ot_util_filename_has_dotdot (const char *path)
-{
- char *p;
- char last;
-
- if (strcmp (path, "..") == 0)
- return TRUE;
- if (g_str_has_prefix (path, "../"))
- return TRUE;
- p = strstr (path, "/..");
- if (!p)
- return FALSE;
- last = *(p + 1);
- return last == '\0' || last == '/';
-}
-
-GPtrArray *
-ot_util_path_split (const char *path)
-{
- GPtrArray *ret = NULL;
- const char *p;
- const char *slash;
- int i;
-
- g_return_val_if_fail (path[0] != '/', NULL);
-
- ret = g_ptr_array_new ();
- g_ptr_array_set_free_func (ret, g_free);
-
- p = path;
- do {
- slash = strchr (p, '/');
- if (!slash)
- {
- g_ptr_array_add (ret, g_strdup (p));
- p = NULL;
- }
- else
- {
- g_ptr_array_add (ret, g_strndup (p, slash - p));
- p = slash + 1;
- }
- } while (p && *p);
-
- /* Canonicalize by removing duplicate '.' */
- for (i = ret->len-1; i >= 0; i--)
- {
- if (strcmp (ret->pdata[i], ".") == 0)
- g_ptr_array_remove_index (ret, i);
- }
-
- return ret;
-}
-
-char *
-ot_util_path_join_n (const char *base, GPtrArray *components, int n)
-{
- int max = MIN(n+1, components->len);
- GPtrArray *subcomponents;
- char *path;
- int i;
-
- subcomponents = g_ptr_array_new ();
-
- if (base != NULL)
- g_ptr_array_add (subcomponents, (char*)base);
-
- for (i = 0; i < max; i++)
- {
- g_ptr_array_add (subcomponents, components->pdata[i]);
- }
- g_ptr_array_add (subcomponents, NULL);
-
- path = g_build_filenamev ((char**)subcomponents->pdata);
- g_ptr_array_free (subcomponents, TRUE);
-
- return path;
-}
-
-void
-ot_util_set_error_from_errno (GError **error,
- gint saved_errno)
-{
- g_set_error_literal (error,
- G_IO_ERROR,
- 0,
- g_strerror (saved_errno));
- errno = saved_errno;
-}
-
-int
-ot_util_open_file_read (const char *path, GError **error)
-{
- char *dirname = NULL;
- char *basename = NULL;
- DIR *dir = NULL;
- int fd = -1;
-
- dirname = g_path_get_dirname (path);
- basename = g_path_get_basename (path);
- dir = opendir (dirname);
- if (dir == NULL)
- {
- ot_util_set_error_from_errno (error, errno);
- goto out;
- }
-
- fd = ot_util_open_file_read_at (dirfd (dir), basename, error);
-
- out:
- g_free (basename);
- g_free (dirname);
- if (dir != NULL)
- closedir (dir);
- return fd;
-}
-
-int
-ot_util_open_file_read_at (int dirfd, const char *name, GError **error)
-{
- int fd;
- int flags = O_RDONLY;
-
-#ifdef O_CLOEXEC
- flags |= O_CLOEXEC;
-#endif
-#ifdef O_NOATIME
- flags |= O_NOATIME;
-#endif
- fd = openat (dirfd, name, flags);
- if (fd < 0)
- ot_util_set_error_from_errno (error, errno);
- return fd;
-}
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#ifndef __OSTREE_UNIX_UTILS_H__
-#define __OSTREE_UNIX_UTILS_H__
-
-#include <gio/gio.h>
-
-/* I just put all this shit here. Sue me. */
-#include <sys/types.h>
-#include <errno.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <dirent.h>
-#include <string.h>
-#include <fcntl.h>
-#include <stdio.h>
-
-G_BEGIN_DECLS
-
-gboolean ot_util_spawn_pager (GOutputStream **out_stream, GError **error);
-
-gboolean ot_util_filename_has_dotdot (const char *path);
-
-GPtrArray *ot_util_sort_filenames_by_component_length (GPtrArray *files);
-
-GPtrArray* ot_util_path_split (const char *path);
-
-char *ot_util_path_join_n (const char *base, GPtrArray *components, int n);
-
-int ot_util_count_filename_components (const char *path);
-
-int ot_util_open_file_read (const char *path, GError **error);
-
-int ot_util_open_file_read_at (int dirfd, const char *name, GError **error);
-
-void ot_util_set_error_from_errno (GError **error, gint saved_errno);
-
-G_END_DECLS
-
-#endif
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#ifndef __OSTREE_UTIL_H__
-
-#include <ot-unix-utils.h>
-#include <ot-gio-utils.h>
-#include <ot-opt-utils.h>
-
-#endif
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include <gio/gio.h>
-
-#include <string.h>
-
-#include "ot-builtins.h"
-
-static OstreeBuiltin builtins[] = {
- { "checkout", ostree_builtin_checkout, 0 },
- { "init", ostree_builtin_init, 0 },
- { "commit", ostree_builtin_commit, 0 },
- { "link-file", ostree_builtin_link_file, 0 },
- { "log", ostree_builtin_log, 0 },
- { "pull", ostree_builtin_pull, 0 },
- { "fsck", ostree_builtin_fsck, 0 },
- { "remote", ostree_builtin_remote, 0 },
- { "rev-parse", ostree_builtin_rev_parse, 0 },
- { "remote", ostree_builtin_remote, 0 },
- { "run-triggers", ostree_builtin_run_triggers, 0 },
- { "show", ostree_builtin_show, 0 },
- { NULL }
-};
-
-static int
-usage (char **argv, gboolean is_error)
-{
- OstreeBuiltin *builtin = builtins;
- void (*print_func) (const gchar *format, ...);
-
- if (is_error)
- print_func = g_printerr;
- else
- print_func = g_print;
-
- print_func ("usage: %s COMMAND [options]\n",
- argv[0]);
- print_func ("Builtin commands:\n");
-
- while (builtin->name)
- {
- print_func (" %s\n", builtin->name);
- builtin++;
- }
- return (is_error ? 1 : 0);
-}
-
-
-int
-main (int argc,
- char **argv)
-{
- OstreeBuiltin *builtin;
- const char *cmd;
-
- g_type_init ();
-
- g_set_prgname (argv[0]);
-
- builtin = builtins;
-
- if (argc < 2)
- return usage (argv, 1);
-
- cmd = argv[1];
-
- while (builtin->name)
- {
- GError *error = NULL;
- if (strcmp (cmd, builtin->name) == 0)
- {
- int i;
- int tmp_argc;
- char **tmp_argv;
-
- tmp_argc = argc - 1;
- tmp_argv = g_new0 (char *, tmp_argc + 1);
-
- tmp_argv[0] = (char*)builtin->name;
- for (i = 0; i < tmp_argc; i++)
- tmp_argv[i+1] = argv[i+2];
- if (!builtin->fn (tmp_argc, tmp_argv, NULL, &error))
- {
- g_free (tmp_argv);
- g_printerr ("%s\n", error->message);
- g_clear_error (&error);
- return 1;
- }
- g_free (tmp_argv);
- return 0;
- }
- builtin++;
- }
-
- g_printerr ("Unknown command '%s'\n", cmd);
- return usage (argv, 1);
-}
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-static char *repo_path;
-
-static GOptionEntry options[] = {
- { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" },
- { NULL }
-};
-
-gboolean
-ostree_builtin_checkout (int argc, char **argv, const char *prefix, GError **error)
-{
- GOptionContext *context;
- gboolean ret = FALSE;
- OstreeRepo *repo = NULL;
- OstreeCheckout *checkout = NULL;
- const char *commit;
- const char *destination;
-
- context = g_option_context_new ("COMMIT DESTINATION - Check out a commit into a filesystem tree");
- g_option_context_add_main_entries (context, options, NULL);
-
- if (!g_option_context_parse (context, &argc, &argv, error))
- goto out;
-
- if (repo_path == NULL)
- repo_path = ".";
-
- repo = ostree_repo_new (repo_path);
- if (!ostree_repo_check (repo, error))
- goto out;
-
- if (argc < 3)
- {
- gchar *help = g_option_context_get_help (context, TRUE, NULL);
- g_printerr ("%s\n", help);
- g_free (help);
- g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "COMMIT and DESTINATION must be specified");
- goto out;
- }
-
- commit = argv[1];
- destination = argv[2];
-
- if (!ostree_repo_checkout (repo, commit, destination, error))
- goto out;
-
- ret = TRUE;
- out:
- if (context)
- g_option_context_free (context);
- g_clear_object (&repo);
- g_clear_object (&checkout);
- return ret;
-}
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-static char *repo_path;
-static gboolean separator_null;
-static int from_fd = -1;
-static gboolean from_stdin;
-static char *from_file;
-static char *subject;
-static char *body;
-static char *parent;
-static char *branch;
-static char **additions;
-static char **removals;
-
-static GOptionEntry options[] = {
- { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" },
- { "subject", 's', 0, G_OPTION_ARG_STRING, &subject, "One line subject", "subject" },
- { "body", 'm', 0, G_OPTION_ARG_STRING, &body, "Full description", "body" },
- { "branch", 'b', 0, G_OPTION_ARG_STRING, &branch, "Branch", "branch" },
- { "parent", 'p', 0, G_OPTION_ARG_STRING, &parent, "Parent commit", "commit" },
- { "from-fd", 0, 0, G_OPTION_ARG_INT, &from_fd, "Read new tree files from fd", "file descriptor" },
- { "from-stdin", 0, 0, G_OPTION_ARG_NONE, &from_stdin, "Read new tree files from stdin", "file descriptor" },
- { "from-file", 0, 0, G_OPTION_ARG_FILENAME, &from_file, "Read new tree files from another file", "path" },
- { "separator-null", 0, 0, G_OPTION_ARG_NONE, &separator_null, "", "Use '\\0' as filename separator, as with find -print0" },
- { "add", 'a', 0, G_OPTION_ARG_FILENAME_ARRAY, &additions, "Relative file path to add", "filename" },
- { "remove", 'r', 0, G_OPTION_ARG_FILENAME_ARRAY, &removals, "Relative file path to remove", "filename" },
- { NULL }
-};
-
-gboolean
-ostree_builtin_commit (int argc, char **argv, const char *prefix, GError **error)
-{
- GOptionContext *context;
- gboolean ret = FALSE;
- OstreeRepo *repo = NULL;
- gboolean using_filename_cmdline;
- gboolean using_filedescriptors;
- GPtrArray *additions_array = NULL;
- GPtrArray *removals_array = NULL;
- GChecksum *commit_checksum = NULL;
- char **iter;
-
- context = g_option_context_new ("- Commit a new revision");
- g_option_context_add_main_entries (context, options, NULL);
-
- if (!g_option_context_parse (context, &argc, &argv, error))
- goto out;
-
- if (repo_path == NULL)
- repo_path = ".";
- if (prefix == NULL)
- prefix = ".";
-
- repo = ostree_repo_new (repo_path);
- if (!ostree_repo_check (repo, error))
- goto out;
-
- using_filename_cmdline = (removals || additions);
- using_filedescriptors = (from_file || from_fd >= 0 || from_stdin);
-
- if (!(using_filename_cmdline || using_filedescriptors))
- {
- g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "No additions or removals specified");
- goto out;
- }
- if (using_filename_cmdline && using_filedescriptors)
- {
- g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "File descriptors may not be combined with --add or --remove");
- goto out;
- }
-
- if (!branch)
- {
- g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "A branch must be specified with --branch");
- goto out;
- }
-
- if (!subject)
- {
- g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "A subject must be specified with --subject");
- goto out;
- }
-
- if (using_filename_cmdline)
- {
- g_assert (removals || additions);
- additions_array = g_ptr_array_new ();
- removals_array = g_ptr_array_new ();
-
- if (additions)
- for (iter = additions; *iter; iter++)
- g_ptr_array_add (additions_array, *iter);
- if (removals)
- for (iter = removals; *iter; iter++)
- g_ptr_array_add (removals_array, *iter);
-
- if (!ostree_repo_commit (repo, branch, parent, subject, body, NULL,
- prefix, additions_array,
- removals_array,
- &commit_checksum,
- error))
- goto out;
- }
- else if (using_filedescriptors)
- {
- char separator = separator_null ? '\0' : '\n';
- gboolean temp_fd = -1;
-
- if (from_stdin)
- from_fd = 0;
- else if (from_file)
- {
- temp_fd = ot_util_open_file_read (from_file, error);
- if (temp_fd < 0)
- {
- g_prefix_error (error, "Failed to open '%s': ", from_file);
- goto out;
- }
- from_fd = temp_fd;
- }
- if (!ostree_repo_commit_from_filelist_fd (repo, branch, parent, subject, body, NULL,
- prefix, from_fd, separator,
- &commit_checksum, error))
- {
- if (temp_fd >= 0)
- close (temp_fd);
- goto out;
- }
- if (temp_fd >= 0)
- close (temp_fd);
- }
-
- ret = TRUE;
- g_print ("%s\n", g_checksum_get_string (commit_checksum));
- out:
- if (context)
- g_option_context_free (context);
- g_clear_object (&repo);
- if (removals_array)
- g_ptr_array_free (removals_array, TRUE);
- if (additions_array)
- g_ptr_array_free (additions_array, TRUE);
- if (commit_checksum)
- g_checksum_free (commit_checksum);
- return ret;
-}
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-static char *repo_path;
-static gboolean quiet;
-
-static GOptionEntry options[] = {
- { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", NULL },
- { "quiet", 'q', 0, G_OPTION_ARG_NONE, &quiet, "Don't display informational messages", NULL },
- { NULL }
-};
-
-typedef struct {
- guint n_objects;
-} HtFsckData;
-
-static gboolean
-checksum_packed_file (HtFsckData *data,
- const char *path,
- GChecksum **out_checksum,
- GError **error)
-{
- gboolean ret = FALSE;
- GChecksum *ret_checksum = NULL;
- GFile *file = NULL;
- char *metadata_buf = NULL;
- GVariant *metadata = NULL;
- GVariant *xattrs = NULL;
- GFileInputStream *in = NULL;
- guint32 metadata_len;
- guint32 version, uid, gid, mode;
- guint64 content_len;
- gsize bytes_read;
- char buf[8192];
-
- file = ot_util_new_file_for_path (path);
-
- in = g_file_read (file, NULL, error);
- if (!in)
- goto out;
-
- if (!g_input_stream_read_all ((GInputStream*)in, &metadata_len, 4, &bytes_read, NULL, error))
- goto out;
-
- metadata_len = GUINT32_FROM_BE (metadata_len);
-
- metadata_buf = g_malloc (metadata_len);
-
- if (!g_input_stream_read_all ((GInputStream*)in, metadata_buf, metadata_len, &bytes_read, NULL, error))
- goto out;
-
- metadata = g_variant_new_from_data (G_VARIANT_TYPE (OSTREE_PACK_FILE_VARIANT_FORMAT),
- metadata_buf, metadata_len, FALSE, NULL, NULL);
-
- g_variant_get (metadata, "(uuuu@a(ayay)t)",
- &version, &uid, &gid, &mode,
- &xattrs, &content_len);
- uid = GUINT32_FROM_BE (uid);
- gid = GUINT32_FROM_BE (gid);
- mode = GUINT32_FROM_BE (mode);
- content_len = GUINT64_FROM_BE (content_len);
-
- ret_checksum = g_checksum_new (G_CHECKSUM_SHA256);
-
- do
- {
- if (!g_input_stream_read_all ((GInputStream*)in, buf, sizeof(buf), &bytes_read, NULL, error))
- goto out;
- g_checksum_update (ret_checksum, (guint8*)buf, bytes_read);
- }
- while (bytes_read > 0);
-
- ostree_checksum_update_stat (ret_checksum, uid, gid, mode);
- g_checksum_update (ret_checksum, (guint8*)g_variant_get_data (xattrs), g_variant_get_size (xattrs));
-
- ret = TRUE;
- *out_checksum = ret_checksum;
- ret_checksum = NULL;
- out:
- if (ret_checksum)
- g_checksum_free (ret_checksum);
- g_free (metadata_buf);
- g_clear_object (&file);
- g_clear_object (&in);
- if (metadata)
- g_variant_unref (metadata);
- if (xattrs)
- g_variant_unref (xattrs);
- return ret;
-}
-
-
-static void
-object_iter_callback (OstreeRepo *repo,
- const char *path,
- GFileInfo *file_info,
- gpointer user_data)
-{
- HtFsckData *data = user_data;
- struct stat stbuf;
- GChecksum *checksum = NULL;
- GError *error = NULL;
- char *dirname = NULL;
- char *checksum_prefix = NULL;
- char *checksum_string = NULL;
- char *filename_checksum = NULL;
- gboolean packed = FALSE;
- OstreeObjectType objtype;
- char *dot;
-
- /* nlinks = g_file_info_get_attribute_uint32 (file_info, "unix::nlink");
- if (nlinks < 2 && !quiet)
- g_printerr ("note: floating object: %s\n", path); */
-
- if (g_str_has_suffix (path, ".meta"))
- objtype = OSTREE_OBJECT_TYPE_META;
- else if (g_str_has_suffix (path, ".file"))
- objtype = OSTREE_OBJECT_TYPE_FILE;
- else if (g_str_has_suffix (path, ".packfile"))
- {
- objtype = OSTREE_OBJECT_TYPE_FILE;
- packed = TRUE;
- }
- else
- g_assert_not_reached ();
-
- if (packed && objtype == OSTREE_OBJECT_TYPE_FILE)
- {
- if (!checksum_packed_file (data, path, &checksum, &error))
- goto out;
- }
- else
- {
- if (!ostree_stat_and_checksum_file (-1, path, objtype, &checksum, &stbuf, &error))
- goto out;
- }
-
- filename_checksum = g_strdup (g_file_info_get_name (file_info));
- dot = strrchr (filename_checksum, '.');
- g_assert (dot != NULL);
- *dot = '\0';
-
- dirname = g_path_get_dirname (path);
- checksum_prefix = g_path_get_basename (dirname);
- checksum_string = g_strconcat (checksum_prefix, filename_checksum, NULL);
-
- if (strcmp (checksum_string, g_checksum_get_string (checksum)) != 0)
- {
- g_printerr ("ERROR: corrupted object '%s' expected checksum: %s\n",
- path, g_checksum_get_string (checksum));
- }
-
- data->n_objects++;
-
- out:
- if (checksum != NULL)
- g_checksum_free (checksum);
- g_free (dirname);
- g_free (checksum_prefix);
- g_free (checksum_string);
- g_free (filename_checksum);
- if (error != NULL)
- {
- g_printerr ("%s\n", error->message);
- g_clear_error (&error);
- }
-}
-
-gboolean
-ostree_builtin_fsck (int argc, char **argv, const char *prefix, GError **error)
-{
- GOptionContext *context;
- HtFsckData data;
- gboolean ret = FALSE;
- OstreeRepo *repo = NULL;
-
- context = g_option_context_new ("- Check the repository for consistency");
- g_option_context_add_main_entries (context, options, NULL);
-
- if (!g_option_context_parse (context, &argc, &argv, error))
- goto out;
-
- if (repo_path == NULL)
- repo_path = ".";
-
- data.n_objects = 0;
-
- repo = ostree_repo_new (repo_path);
- if (!ostree_repo_check (repo, error))
- goto out;
-
- if (!ostree_repo_iter_objects (repo, object_iter_callback, &data, error))
- goto out;
-
- if (!quiet)
- g_printerr ("Total Objects: %u\n", data.n_objects);
-
- ret = TRUE;
- out:
- if (context)
- g_option_context_free (context);
- g_clear_object (&repo);
- return ret;
-}
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-static char *repo_path;
-static gboolean archive;
-
-static GOptionEntry options[] = {
- { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", NULL },
- { "archive", 0, 0, G_OPTION_ARG_NONE, &archive, "Initialize repository as archive", NULL },
- { NULL }
-};
-
-#define DEFAULT_CONFIG_CONTENTS ("[core]\n" \
- "repo_version=0\n")
-
-
-gboolean
-ostree_builtin_init (int argc, char **argv, const char *prefix, GError **error)
-{
- GOptionContext *context = NULL;
- gboolean ret = FALSE;
- GFile *repodir = NULL;
- GFile *child = NULL;
- GFile *grandchild = NULL;
- GString *config_data = NULL;
-
- context = g_option_context_new ("- Initialize a new empty repository");
- g_option_context_add_main_entries (context, options, NULL);
-
- if (!g_option_context_parse (context, &argc, &argv, error))
- goto out;
-
- if (repo_path == NULL)
- repo_path = ".";
-
- repodir = ot_util_new_file_for_path (repo_path);
-
- child = g_file_get_child (repodir, "config");
-
- config_data = g_string_new (DEFAULT_CONFIG_CONTENTS);
- g_string_append_printf (config_data, "archive=%s\n", archive ? "true" : "false");
- if (!g_file_replace_contents (child,
- config_data->str,
- config_data->len,
- NULL, FALSE, 0, NULL,
- NULL, error))
- goto out;
- g_clear_object (&child);
-
- child = g_file_get_child (repodir, "objects");
- if (!g_file_make_directory (child, NULL, error))
- goto out;
- g_clear_object (&child);
-
- child = g_file_get_child (repodir, "refs");
- if (!g_file_make_directory (child, NULL, error))
- goto out;
-
- grandchild = g_file_get_child (child, "heads");
- if (!g_file_make_directory (grandchild, NULL, error))
- goto out;
- g_clear_object (&grandchild);
-
- grandchild = g_file_get_child (child, "remotes");
- if (!g_file_make_directory (grandchild, NULL, error))
- goto out;
- g_clear_object (&grandchild);
-
- g_clear_object (&child);
-
- child = g_file_get_child (repodir, "tags");
- if (!g_file_make_directory (child, NULL, error))
- goto out;
- g_clear_object (&child);
-
- ret = TRUE;
- out:
- if (context)
- g_option_context_free (context);
- if (config_data)
- g_string_free (config_data, TRUE);
- g_clear_object (&repodir);
- g_clear_object (&child);
- g_clear_object (&grandchild);
- return ret;
-}
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-static char *repo_path;
-static gboolean ignore_exists;
-static gboolean force;
-
-static GOptionEntry options[] = {
- { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" },
- { "ignore-exists", 'n', 0, G_OPTION_ARG_NONE, &ignore_exists, "Don't error if file exists", NULL },
- { "force", 'f', 0, G_OPTION_ARG_NONE, &force, "If object exists, relink file", NULL },
- { NULL }
-};
-
-gboolean
-ostree_builtin_link_file (int argc, char **argv, const char *prefix, GError **error)
-{
- GOptionContext *context;
- gboolean ret = FALSE;
- OstreeRepo *repo = NULL;
- int i;
-
- context = g_option_context_new ("- Create a new hard link in the repository");
- g_option_context_add_main_entries (context, options, NULL);
-
- if (!g_option_context_parse (context, &argc, &argv, error))
- goto out;
-
- if (repo_path == NULL)
- repo_path = ".";
-
- repo = ostree_repo_new (repo_path);
- if (!ostree_repo_check (repo, error))
- goto out;
-
- for (i = 0; i < argc-1; i++)
- {
- if (!ostree_repo_link_file (repo, argv[i+1], ignore_exists, force, error))
- goto out;
- }
-
- ret = TRUE;
- out:
- if (context)
- g_option_context_free (context);
- g_clear_object (&repo);
- return ret;
-}
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-static char *repo_path;
-
-static GOptionEntry options[] = {
- { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" },
- { NULL }
-};
-
-gboolean
-ostree_builtin_log (int argc, char **argv, const char *prefix, GError **error)
-{
- GOptionContext *context;
- gboolean ret = FALSE;
- OstreeRepo *repo = NULL;
- GOutputStream *pager = NULL;
- const char *rev;
- GVariant *commit = NULL;
- char *resolved_rev = NULL;
-
- context = g_option_context_new ("- Show revision log");
- g_option_context_add_main_entries (context, options, NULL);
-
- if (!g_option_context_parse (context, &argc, &argv, error))
- goto out;
-
- if (repo_path == NULL)
- repo_path = ".";
- if (prefix == NULL)
- prefix = ".";
-
- if (argc < 2)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "A revision must be specified");
- goto out;
- }
-
- rev = argv[1];
-
- repo = ostree_repo_new (repo_path);
- if (!ostree_repo_check (repo, error))
- goto out;
-
- if (!ot_util_spawn_pager (&pager, error))
- goto out;
-
- if (!ostree_repo_resolve_rev (repo, rev, &resolved_rev, error))
- goto out;
-
- while (TRUE)
- {
- OstreeSerializedVariantType type;
- char *formatted = NULL;
- guint32 version;
- const char *parent;
- const char *subject;
- const char *body;
- guint64 timestamp;
- const char *contents;
- const char *root_metadata;
- GDateTime *time_obj = NULL;
- char *formatted_date = NULL;
- const char *body_newline;
- gsize bytes_written;
- GVariant *commit_metadata = NULL;
- char *formatted_metadata = NULL;
-
- if (commit)
- g_variant_unref (commit);
- if (!ostree_repo_load_variant (repo, resolved_rev, &type, &commit, error))
- goto out;
-
- /* Ignore commit metadata for now */
- g_variant_get (commit, "(u@a{sv}&s&s&st&s&s)",
- &version, &commit_metadata, &parent, &subject, &body,
- ×tamp, &contents, &root_metadata);
- version = GUINT32_FROM_BE (version);
- timestamp = GUINT64_FROM_BE (timestamp);
- time_obj = g_date_time_new_from_unix_utc (timestamp);
- formatted_date = g_date_time_format (time_obj, "%a %b %d %H:%M:%S %Y %z");
- g_date_time_unref (time_obj);
- time_obj = NULL;
-
- formatted_metadata = g_variant_print (commit_metadata, TRUE);
- g_variant_unref (commit_metadata);
- formatted = g_strdup_printf ("commit %s\nSubject: %s\nDate: %s\nMetadata: %s\n\n",
- resolved_rev, subject, formatted_date, formatted_metadata);
- g_free (formatted_metadata);
- g_free (formatted_date);
- formatted_date = NULL;
-
- if (!g_output_stream_write_all (pager, formatted, strlen (formatted), &bytes_written, NULL, error))
- {
- g_free (formatted);
- goto out;
- }
- g_free (formatted);
-
- body_newline = strchr (body, '\n');
- do {
- gsize len;
- if (!g_output_stream_write_all (pager, " ", 4, &bytes_written, NULL, error))
- goto out;
- len = body_newline ? body_newline - body : strlen (body);
- if (!g_output_stream_write_all (pager, body, len, &bytes_written, NULL, error))
- goto out;
- if (!g_output_stream_write_all (pager, "\n\n", 2, &bytes_written, NULL, error))
- goto out;
- body_newline = strchr (body, '\n');
- if (!body_newline)
- break;
- else
- body_newline += 1;
- } while (*body_newline);
-
- if (strcmp (parent, "") == 0)
- break;
- g_free (resolved_rev);
- resolved_rev = g_strdup (parent);
- }
-
- if (!g_output_stream_close (pager, NULL, error))
- goto out;
-
- ret = TRUE;
- out:
- g_free (resolved_rev);
- if (context)
- g_option_context_free (context);
- if (commit)
- g_variant_unref (commit);
- g_clear_object (&repo);
- return ret;
-}
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-#include <libsoup/soup-gnome.h>
-
-static char *repo_path;
-
-static GOptionEntry options[] = {
- { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" },
- { NULL }
-};
-
-static gboolean
-fetch_uri (OstreeRepo *repo,
- SoupSession *soup,
- SoupURI *uri,
- char **temp_filename,
- GError **error)
-{
- gboolean ret = FALSE;
- SoupMessage *msg = NULL;
- guint response;
- char *template = NULL;
- int fd;
- SoupBuffer *buf = NULL;
- GFile *tempf = NULL;
-
- msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
-
- response = soup_session_send_message (soup, msg);
- if (response != 200)
- {
- char *uri_string = soup_uri_to_string (uri, FALSE);
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Failed to retrieve '%s': %d %s",
- uri_string, response, msg->reason_phrase);
- g_free (uri_string);
- goto out;
- }
-
- template = g_strdup_printf ("%s/tmp-fetchXXXXXX", ostree_repo_get_path (repo));
-
- fd = g_mkstemp (template);
- if (fd < 0)
- {
- ot_util_set_error_from_errno (error, errno);
- goto out;
- }
- close (fd);
- tempf = ot_util_new_file_for_path (template);
-
- buf = soup_message_body_flatten (msg->response_body);
-
- if (!g_file_replace_contents (tempf, buf->data, buf->length, NULL, FALSE, 0, NULL, NULL, error))
- goto out;
-
- *temp_filename = template;
- template = NULL;
-
- ret = TRUE;
- out:
- g_free (template);
- g_clear_object (&msg);
- g_clear_object (&tempf);
- return ret;
-}
-
-static gboolean
-store_object (OstreeRepo *repo,
- SoupSession *soup,
- SoupURI *baseuri,
- const char *object,
- OstreeObjectType objtype,
- gboolean *did_exist,
- GError **error)
-{
- gboolean ret = FALSE;
- char *filename = NULL;
- char *objpath = NULL;
- char *relpath = NULL;
- SoupURI *obj_uri = NULL;
-
- objpath = ostree_get_relative_object_path (object, objtype, TRUE);
- obj_uri = soup_uri_copy (baseuri);
- relpath = g_build_filename (soup_uri_get_path (obj_uri), objpath, NULL);
- soup_uri_set_path (obj_uri, relpath);
-
- if (!fetch_uri (repo, soup, obj_uri, &filename, error))
- goto out;
-
- if (!ostree_repo_store_packfile (repo, object, filename, objtype, error))
- goto out;
-
- ret = TRUE;
- out:
- if (obj_uri)
- soup_uri_free (obj_uri);
- if (filename)
- (void) unlink (filename);
- g_free (filename);
- g_free (objpath);
- g_free (relpath);
- return ret;
-}
-
-static gboolean
-store_tree_recurse (OstreeRepo *repo,
- SoupSession *soup,
- SoupURI *base_uri,
- const char *rev,
- GError **error)
-{
- gboolean ret = FALSE;
- GVariant *tree = NULL;
- GVariant *files_variant = NULL;
- GVariant *dirs_variant = NULL;
- OstreeSerializedVariantType metatype;
- gboolean did_exist;
- int i, n;
-
- if (!store_object (repo, soup, base_uri, rev, OSTREE_OBJECT_TYPE_META, &did_exist, error))
- goto out;
-
- if (!did_exist)
- {
- if (!ostree_repo_load_variant (repo, rev, &metatype, &tree, error))
- goto out;
-
- if (metatype != OSTREE_SERIALIZED_TREE_VARIANT)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Tree metadata '%s' has wrong type %d, expected %d",
- rev, metatype, OSTREE_SERIALIZED_TREE_VARIANT);
- goto out;
- }
-
- /* PARSE OSTREE_SERIALIZED_TREE_VARIANT */
- g_variant_get_child (tree, 2, "@a(ss)", &files_variant);
- g_variant_get_child (tree, 3, "@a(sss)", &dirs_variant);
-
- n = g_variant_n_children (files_variant);
- for (i = 0; i < n; i++)
- {
- const char *filename;
- const char *checksum;
-
- g_variant_get_child (files_variant, i, "(ss)", &filename, &checksum);
-
- if (!store_object (repo, soup, base_uri, checksum, OSTREE_OBJECT_TYPE_FILE, &did_exist, error))
- goto out;
- }
-
- for (i = 0; i < n; i++)
- {
- const char *dirname;
- const char *tree_checksum;
- const char *meta_checksum;
-
- g_variant_get_child (dirs_variant, i, "(sss)",
- &dirname, &tree_checksum, &meta_checksum);
-
- if (!store_tree_recurse (repo, soup, base_uri, tree_checksum, error))
- goto out;
-
- if (!store_object (repo, soup, base_uri, meta_checksum, OSTREE_OBJECT_TYPE_META, &did_exist, error))
- goto out;
- }
- }
-
- ret = TRUE;
- out:
- if (tree)
- g_variant_unref (tree);
- if (files_variant)
- g_variant_unref (files_variant);
- if (dirs_variant)
- g_variant_unref (dirs_variant);
- return ret;
-}
-
-static gboolean
-store_commit_recurse (OstreeRepo *repo,
- SoupSession *soup,
- SoupURI *base_uri,
- const char *rev,
- GError **error)
-{
- gboolean ret = FALSE;
- GVariant *commit = NULL;
- OstreeSerializedVariantType metatype;
- const char *tree_contents_checksum;
- const char *tree_meta_checksum;
- gboolean did_exist;
-
- if (!store_object (repo, soup, base_uri, rev, OSTREE_OBJECT_TYPE_META, &did_exist, error))
- goto out;
-
- if (!did_exist)
- {
- if (!ostree_repo_load_variant (repo, rev, &metatype, &commit, error))
- goto out;
-
- if (metatype != OSTREE_SERIALIZED_COMMIT_VARIANT)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Commit '%s' has wrong type %d, expected %d",
- rev, metatype, OSTREE_SERIALIZED_COMMIT_VARIANT);
- goto out;
- }
-
- /* PARSE OSTREE_SERIALIZED_COMMIT_VARIANT */
- g_variant_get_child (commit, 6, "&s", &tree_contents_checksum);
- g_variant_get_child (commit, 7, "&s", &tree_meta_checksum);
-
- if (!store_object (repo, soup, base_uri, tree_meta_checksum, OSTREE_OBJECT_TYPE_META, &did_exist, error))
- goto out;
-
- if (!store_tree_recurse (repo, soup, base_uri, tree_contents_checksum, error))
- goto out;
- }
-
- ret = TRUE;
- out:
- if (commit)
- g_variant_unref (commit);
- return ret;
-}
-
-gboolean
-ostree_builtin_pull (int argc, char **argv, const char *prefix, GError **error)
-{
- GOptionContext *context;
- gboolean ret = FALSE;
- OstreeRepo *repo = NULL;
- const char *remote;
- const char *branch;
- char *remote_branch_ref_path = NULL;
- char *key = NULL;
- char *baseurl = NULL;
- char *refpath = NULL;
- char *temppath = NULL;
- GKeyFile *config = NULL;
- SoupURI *base_uri = NULL;
- SoupURI *target_uri = NULL;
- SoupSession *soup = NULL;
- char *rev = NULL;
-
- context = g_option_context_new ("REMOTE BRANCH - Download data from remote repository");
- g_option_context_add_main_entries (context, options, NULL);
-
- if (!g_option_context_parse (context, &argc, &argv, error))
- goto out;
-
- if (repo_path == NULL)
- repo_path = ".";
-
- repo = ostree_repo_new (repo_path);
- if (!ostree_repo_check (repo, error))
- goto out;
-
- if (argc < 3)
- {
- ot_util_usage_error (context, "REMOTE and BRANCH must be specified", error);
- goto out;
- }
-
- remote = argv[1];
- branch = argv[2];
-
- config = ostree_repo_get_config (repo);
-
- key = g_strdup_printf ("remote \"%s\"", remote);
- baseurl = g_key_file_get_string (config, key, "url", error);
- if (!baseurl)
- goto out;
- base_uri = soup_uri_new (baseurl);
- if (!base_uri)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Failed to parse url '%s'", baseurl);
- goto out;
- }
- target_uri = soup_uri_copy (base_uri);
- g_free (refpath);
- refpath = g_build_filename (soup_uri_get_path (target_uri), "refs", "heads", branch, NULL);
- soup_uri_set_path (target_uri, refpath);
-
- soup = soup_session_sync_new_with_options (SOUP_SESSION_USER_AGENT, "ostree ",
- SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_GNOME_FEATURES_2_26,
- SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_CONTENT_DECODER,
- SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_COOKIE_JAR,
- NULL);
- if (!fetch_uri (repo, soup, target_uri, &temppath, error))
- goto out;
-
- rev = ot_util_get_file_contents_utf8 (temppath, error);
- if (!rev)
- goto out;
- g_strchomp (rev);
-
- if (!ostree_validate_checksum_string (rev, error))
- goto out;
-
- if (!store_commit_recurse (repo, soup, base_uri, rev, error))
- goto out;
-
- if (!ostree_repo_write_ref (repo, FALSE, branch, rev, error))
- goto out;
-
- ret = TRUE;
- out:
- if (context)
- g_option_context_free (context);
- if (temppath)
- (void) unlink (temppath);
- g_free (temppath);
- g_free (key);
- g_free (rev);
- g_free (baseurl);
- g_free (refpath);
- g_free (remote_branch_ref_path);
- g_clear_object (&soup);
- if (base_uri)
- soup_uri_free (base_uri);
- if (target_uri)
- soup_uri_free (target_uri);
- g_clear_object (&repo);
- g_clear_object (&soup);
- return ret;
-}
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-static char *repo_path;
-
-static GOptionEntry options[] = {
- { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" },
- { NULL }
-};
-
-static void
-usage_error (GOptionContext *context, const char *message, GError **error)
-{
- gchar *help = g_option_context_get_help (context, TRUE, NULL);
- g_printerr ("%s\n", help);
- g_free (help);
- g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- message);
-}
-
-gboolean
-ostree_builtin_remote (int argc, char **argv, const char *prefix, GError **error)
-{
- GOptionContext *context;
- gboolean ret = FALSE;
- OstreeRepo *repo = NULL;
- OstreeCheckout *checkout = NULL;
- const char *op;
- GKeyFile *config = NULL;
-
- context = g_option_context_new ("OPERATION [args] - Control remote repository configuration");
- g_option_context_add_main_entries (context, options, NULL);
-
- if (!g_option_context_parse (context, &argc, &argv, error))
- goto out;
-
- if (repo_path == NULL)
- repo_path = ".";
-
- repo = ostree_repo_new (repo_path);
- if (!ostree_repo_check (repo, error))
- goto out;
-
- if (argc < 2)
- {
- usage_error (context, "OPERATION must be specified", error);
- goto out;
- }
-
- op = argv[1];
-
- config = ostree_repo_copy_config (repo);
-
- if (!strcmp (op, "add"))
- {
- char *key;
- if (argc < 4)
- {
- usage_error (context, "NAME and URL must be specified", error);
- goto out;
- }
- key = g_strdup_printf ("remote \"%s\"", argv[2]);
- g_key_file_set_string (config, key, "url", argv[3]);
- g_free (key);
- }
- else
- {
- usage_error (context, "Unknown operation", error);
- goto out;
- }
-
- if (!ostree_repo_write_config (repo, config, error))
- goto out;
-
- ret = TRUE;
- out:
- if (context)
- g_option_context_free (context);
- if (config)
- g_key_file_unref (config);
- g_clear_object (&repo);
- g_clear_object (&checkout);
- return ret;
-}
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-static char *repo_path;
-
-static GOptionEntry options[] = {
- { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" },
- { NULL }
-};
-
-gboolean
-ostree_builtin_rev_parse (int argc, char **argv, const char *prefix, GError **error)
-{
- GOptionContext *context;
- gboolean ret = FALSE;
- OstreeRepo *repo = NULL;
- const char *rev = "master";
- char *resolved_rev = NULL;
- GVariant *variant = NULL;
- char *formatted_variant = NULL;
-
- context = g_option_context_new ("REV - Output the target of a rev");
- g_option_context_add_main_entries (context, options, NULL);
-
- if (!g_option_context_parse (context, &argc, &argv, error))
- goto out;
-
- if (repo_path == NULL)
- repo_path = ".";
-
- repo = ostree_repo_new (repo_path);
- if (!ostree_repo_check (repo, error))
- goto out;
-
- if (argc < 2)
- {
- ot_util_usage_error (context, "REV must be specified", error);
- goto out;
- }
- rev = argv[1];
-
- if (!ostree_repo_resolve_rev (repo, rev, &resolved_rev, error))
- goto out;
-
- g_print ("%s\n", resolved_rev);
-
- ret = TRUE;
- out:
- g_free (resolved_rev);
- if (context)
- g_option_context_free (context);
- g_clear_object (&repo);
- if (variant)
- g_variant_unref (variant);
- g_free (formatted_variant);
- return ret;
-}
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-static char *repo_path;
-static gboolean quiet;
-
-static GOptionEntry options[] = {
- { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" },
- { "quiet", 'q', 0, G_OPTION_ARG_NONE, &quiet, "Don't display informational messages", NULL },
- { NULL }
-};
-
-gboolean
-ostree_builtin_run_triggers (int argc, char **argv, const char *prefix, GError **error)
-{
- GOptionContext *context;
- gboolean ret = FALSE;
- OstreeRepo *repo = NULL;
- OstreeCheckout *checkout = NULL;
- const char *dir;
-
- context = g_option_context_new ("DIR - Run trigger scripts for directory");
- g_option_context_add_main_entries (context, options, NULL);
-
- if (!g_option_context_parse (context, &argc, &argv, error))
- goto out;
-
- if (repo_path == NULL)
- repo_path = ".";
-
- repo = ostree_repo_new (repo_path);
- if (!ostree_repo_check (repo, error))
- goto out;
-
- if (argc < 1)
- {
- gchar *help = g_option_context_get_help (context, TRUE, NULL);
- g_printerr ("%s\n", help);
- g_free (help);
- g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "DIR must be specified");
- goto out;
- }
-
- dir = argv[1];
-
- checkout = ostree_checkout_new (repo, dir);
- if (!ostree_checkout_run_triggers (checkout, error))
- goto out;
-
- ret = TRUE;
- out:
- if (context)
- g_option_context_free (context);
- g_clear_object (&repo);
- g_clear_object (&checkout);
- return ret;
-}
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-static char *repo_path;
-
-static GOptionEntry options[] = {
- { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" },
- { NULL }
-};
-
-gboolean
-ostree_builtin_show (int argc, char **argv, const char *prefix, GError **error)
-{
- GOptionContext *context;
- gboolean ret = FALSE;
- OstreeRepo *repo = NULL;
- const char *rev = "master";
- char *resolved_rev = NULL;
- OstreeSerializedVariantType type;
- GVariant *variant = NULL;
- char *formatted_variant = NULL;
-
- context = g_option_context_new ("- Output a metadata object");
- g_option_context_add_main_entries (context, options, NULL);
-
- if (!g_option_context_parse (context, &argc, &argv, error))
- goto out;
-
- if (repo_path == NULL)
- repo_path = ".";
-
- repo = ostree_repo_new (repo_path);
- if (!ostree_repo_check (repo, error))
- goto out;
-
- if (argc > 1)
- rev = argv[1];
-
- if (!ostree_repo_resolve_rev (repo, rev, &resolved_rev, error))
- goto out;
-
- if (!ostree_repo_load_variant (repo, resolved_rev, &type, &variant, error))
- goto out;
-
- g_print ("Object: %s\nType: %d\n", resolved_rev, type);
- formatted_variant = g_variant_print (variant, TRUE);
- g_print ("%s\n", formatted_variant);
-
- ret = TRUE;
- out:
- g_free (resolved_rev);
- if (context)
- g_option_context_free (context);
- g_clear_object (&repo);
- if (variant)
- g_variant_unref (variant);
- g_free (formatted_variant);
- return ret;
-}
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#ifndef __OSTREE_BUILTINS__
-#define __OSTREE_BUILTINS__
-
-#include <glib-object.h>
-
-G_BEGIN_DECLS
-
-typedef enum {
- OSTREE_BUILTIN_FLAG_NONE = 0,
-} OstreeBuiltinFlags;
-
-typedef struct {
- const char *name;
- gboolean (*fn) (int argc, char **argv, const char *prefix, GError **error);
- int flags; /* OstreeBuiltinFlags */
-} OstreeBuiltin;
-
-gboolean ostree_builtin_checkout (int argc, char **argv, const char *prefix, GError **error);
-gboolean ostree_builtin_commit (int argc, char **argv, const char *prefix, GError **error);
-gboolean ostree_builtin_init (int argc, char **argv, const char *prefix, GError **error);
-gboolean ostree_builtin_log (int argc, char **argv, const char *prefix, GError **error);
-gboolean ostree_builtin_link_file (int argc, char **argv, const char *prefix, GError **error);
-gboolean ostree_builtin_pull (int argc, char **argv, const char *prefix, GError **error);
-gboolean ostree_builtin_run_triggers (int argc, char **argv, const char *prefix, GError **error);
-gboolean ostree_builtin_fsck (int argc, char **argv, const char *prefix, GError **error);
-gboolean ostree_builtin_show (int argc, char **argv, const char *prefix, GError **error);
-gboolean ostree_builtin_rev_parse (int argc, char **argv, const char *prefix, GError **error);
-gboolean ostree_builtin_remote (int argc, char **argv, const char *prefix, GError **error);
-
-G_END_DECLS
-
-#endif